GhaSShee


OCaml


Reference : [Atsushi Igarashi] Programming in OCaml # SML v.s. OCaml [SML v.s. OCaml Comparison](https://people.mpi-sws.org/~rossberg/sml-vs-ocaml.html) ## SML nj I recommend using SML/NJ's Compilation Manager (CM). It's a build system for SML code in the context of SML/NJ. It can get quite complicated if you need more advanced features, but it's easy to get started. I'll show you a barebones structure and you can adjust as needed. It already comes installed with SML/NJ, so there's no installation process. I'll use the following directory structure for this example (the file extensions are not imposed, just a convention): ~~~ sh . ├── build.cm └── src ├── foo.fun ├── foo.sig └── main.sml ~~~ build.cm ~~~ SML group (* CM allows you to selectively export defined modules (structures, signatures and functors) by listing them here. It's useful for libraries. *) source (-) (* export all defined modules *) structure Main (* OR, export selectively *) signature FOO functor Foo is (* Import the SML standard library, aka Basis. *) (* See: http://sml-family.org/Basis/ *) $/basis.cm (* Import the SML/NJ library *) (* Provides extra data structures and algorithms. *) (* See: https://www.smlnj.org/doc/smlnj-lib/Manual/toc.html *) $/smlnj-lib.cm (* List each source file you want to be considered for compilation. *) src/main.sml src/foo.sig src/foo.fun ~~~ src/main.sml ~~~ SML structure Main = struct (* You don't have to import the `Foo` functor. *) (* It's been done in build.cm already. *) structure F = Foo() fun main () = print (F.message ^ "\n") end src/foo.sig signature FOO = sig val message : string end ~~~ src/foo.fun ~~~ (* You don't have to import the `FOO` signature. *) (* It's been done in build.cm already. *) functor Foo() : FOO = struct val message = "Hello, World!" end ~~~ Usage Having the structure in place, you can start compiling using CM.make and running by calling whatever functions you've defined: ~~~ shell $ sml Standard ML of New Jersey v110.82 [built: Tue Jan 9 20:54:02 2018] - CM.make "build.cm"; val it = true : bool - - Main.main (); Hello, World! val it = () : unit ~~~ ## SML lexer smlnj ML-lexer https://www.smlnj.org/doc/ML-Lex/manual.html#sample # custom operators https://blog.shaynefletcher.org/2016/09/custom-operators-in-ocaml.html # pipeline in ocaml Another, clearer way of producing the same result in OCaml uses higher-order functions and the pipeline operator `|>`: ~~~ let square x = x*x let sum = List.fold_left (+) 0 let sum_sq n = 0--n (* [0;1;2;...;n] *) |> List.map square (* [0;1;4;...;n*n] *) |> sum (* 0+1+4+...+n*n *) ~~~ # Prerequisity ## installation * Linux : See https://ghassheee.github.io/posix/ * MacOS : Maybe like this; `brew install opam && opam init && opam install utop` * Windows : Do not use Windows. ## interpreter First, let's run the ocaml interpreter; open a terminal emulator and type as the following; ~~~sh $ ocaml ## or utop ~~~ ## modules In the ocaml interpreter, you can see the definition of modules with; ~~~ > #show Sys ;; > #show_module Unix ;; > #show Pervasives ;; ~~~ ## IO IO functions; ~~~ (* *) open_in : string -> in_channel open_in_gen : open_flag list -> int -> string -> in_channel input_char : in_channel -> char input_line : in_channel -> string input_byte : in_channel -> int close_in : in_channel -> () flush open_out output_char output_string output_byte close_out open_out_gen [Open_wronly; Open_append; Open_text] 0o666 "" //add to last ~~~ IO channels; ~~~ stdin : in_channel stdout : out_channel stderr : out_channel ~~~ # OCaml Low Level Assembly ~~~ $ ocaml -dinstr system OCaml version 4.08.1 ... # let _ = 1;; const 1 return 1 - : int = 1 ~~~ # プログラミング in Ocaml ***************************************************************************** * * * * * * * dynamically typed ---- lisp, ruby, perl * * language * * * * * * statically typed -+-- weekly typed ---- c * * language | * * '-- strongly typed -+-- java * * | * * '-- polymorphic ---- ocaml * * type system * * * * * * * ***************************************************************************** ## Preprocess posix related directives ~~~ #use "file";; (* load file *) #cd "path";; (* change dir *) #directory "path";; (* add a dir to the list that OCaml looks for files in *) ~~~ ## Syntax Expression ~~~ e ::= c // constants | unop e // unit operator | e1 binop e2 // binary operator | if e1 then e2 else e3 | (e) | id // identifier | d in e | id (e1,...,em) // ... ~~~ Declaration ~~~ d ::= let id = e | let id (x1:t1, ..., xn:tn):t = e // here, `e` is called `body` | let rec id (x1:t1, ..., xn:tn):t = e // ... ~~~ Type ~~~ t ::= int | float | bool | string | char | t1 * ... * tn | t1 -> t2 | (t) ... ~~~ e.g. ~~~ let square x = x*x // inplicit type definition (* : int->int->int) let square (x:int) : int = x * x // explicit type definition let fourth y = let square x = x*x in square (squqre y) let hoge i j = if i < j || false then ( if j > i then 5 else 6 ) else ( let f = 3 in f + 4 ) ~~~ ## some MEMO - `Gc.full_major()` - `<-` : assignment ~~~ # let s = "string" and u = "string";; # s == u ;; - : bool = false # s = u ;; - : bool = true ~~~ ~~~ `!=` , `==` : shallow equality `<>` , `=` : deep equality ~~~ - `shallow equality` means that they are the same instance. - `deep equality` means that they are the same contents. # evaluate various expressions !! ## Communicative compiler and Batch compiler directive | motion :-----------------------|:--------------------- `#quit` | quit interpreter `#cd "dir"` | change directory `#use "file"` | load the src code file `#load "file"` | load the byte code object file `#print_depth "int"` | set the depth of displaying `#print_length "int"` | set the length of displaying `trace "function"` | trace the function `untrace "function"` | stop tracing - meta language : a describing language (e.g. english) - object language : a described language (e.g. ocaml) ## Data Types ~~~ (* int *) # (-) 4 5 ;; // - : int = -1 # 7 * -5 ;; // - : int = -35 # 7*-5 ;; // !err (Here *- is regarded as one operator) # - - 1 ;; // - : int = 1 ( - is unit-operator) # + 3 + 5 ;; // - : int = 8 ( first + is unit-op, second + is binary-op ) # 0b1011 ;; // - : int = 11 (binary representation) # max_int ;; // - : int = 4611686018427387903 # 107 mod 7 ;; // - : int = 2 (* float *) # 6.02E23 ;; // - : float = 6.02e+23 # -1e350 ;; // - : float = neg_infinity # 0.0 /. 0.0 ;; // - : float = nan # sqrt (-1.) ;; // - : float = nan # sqrt (-1) ;; // !Type Err # 5.0 ** 2.0;; // - : float = 25. # int_of_float (-0.7);; // - : int = 0 # sin (3.14 /. 2.0 ) ** 2.0 +. cos (3.14 /.2.0) ** 2.0 ;; // - : float = 1. (* char *) # 'a' ;; // - : char = 'a' # '\120' ;; // - : char = 'x' # int_of_char '\'' ;; // - : int = 39 # char_of_int 55 ;; // - : char = '7' # char_of_int ((int_of_char 'A' ) + 20);; // - : char = 'U' (* string *) # "This is a pen." ;; // - : string = "This is a pen." # ( "Hello," ^ "World!" ).[10] ;; // - : char = 'd' # int_of_string "0xff" ;; // - : int = 255 (0xff is int expression) # int_of_string "0b101010" ;; # print_string "\065 - \090 is ALPHABET";; A - Z is ALPHABET- : unit = () ~~~ escape sequence | character :-------------------|:----------------------- `\\` | backslash `\'` | single quotation `\"` | double quotation `\n` | - `\r` | return to the head of the line `\t` | tab `\b` | backspace `\ddd` | `\000` - `\255` ascii code `\xhh` | `\00` - `\ff` ascii code ## naming variable 1. start with `_` | small alphabet 2. then, `[_0-9a-zA-Z]` !! exclude the single character name `_` reserved ~~~ and as assert asr begin class closed contraint do done downto else end exception external flase for fun function functor if in include inherit land lazy let lor lsl lsr lxor match method mod module mutable new of open or parser private rec sig struct then to true try type val virtual when while with ~~~ # functional programming formal argument : 仮引数 ## prefix infix prefix operator `~-` ~~~ abs ~-1 abs (-1) ~~~ ~~~ prefix operator := {!|?|~}{!|$|%|*|+|-|.|/|:|<|=|>|?|@|^|\||~}* infix operator := {$|%|*|+|-|.|/|:|<|=|>|@|\||^}{!|$|%|*|+|-|.|/|:|<|=|>|?|@|^|\||~}* ~~~ operator | association ----------------------------------------------------|------------------------ prefix (except -, -.) | - `.` , `.()`, `.[]`, application | left startwith `**`, `lsl`, `lsr`, `asr` | right startwith `*` `/` `%` , mod, land, lor, lxor | left startwith `+` `-` | left `::` | right startwith `@` `^` | right `=`, `<`, ... | left `&`, `&&` | left `or`, `||` | left `,` (pair) | - `<-`, `:=` ..etc() | right if | - `;` | right `let`, `match`, `fun`, `function`, `try` | - # Polymorphism & Type Inference basically, polymorphism is natural taransformation, which means
`sum` is mere a morphism and `length` is a polymorphism.
poly means it does not depend on what any type A is. ~~~ sum : list Int -> Int length : listr A -> Nat ~~~ polymorphic functions ~~~ fst : A * B -> A snd : A * B -> B id : A -> A apply : Fun(A,B) -> A -> B apply : (A -> B) -> A -> B twice : (A -> A) -> A -> A ~~~ OCaml ~~~ let fst (a,b) = a;; let snd (a,b) = b;; let id a = a;; let apply f a = f a let twice f x = f(f x) let fourtimes x = twice twice x ~~~ !! Notice NOT polymorphic : the following defined function could have a unique type. ~~~ let fourtimes = twice twice ~~~ ## classification of polymorphism - parametric polymorphism ~~ polymorphism - subtyping polymorphism : for all subtype of a type, the morphism can work - ad-hoc polymorphism : as, `+` `=` ..., behavier differs from type to type. # Recursive Data Structure : List ## pattern match expression syntax of pattern ~~~ pattern ::= pure_pattern | pure_pattern when cond ~~~ `function` is suger syntax of `fun x -> match x with` ~~~ function pattern1 -> expr1 | pattern2 when cond -> expr2 | ... ~~~ ~~~ match x with pattern1 when cond1 -> expr1 | pattern2 when cond2 -> expr2 | pattern3 -> expr3 | ... ~~~ ## record type typename = {fieldname_1 : type_1 ; ... ; filename_n : type_n } 同じ名前の組を持つレコードは多重定義できない # Data Structure ## record & variant * Record : named properties in tuple ~~ struct * variant : ~~ union # Exception # IO # modules ## `Printf.printf` & `format` ~~~ # Open Printf ;; # printf ;; - : ('a, out_channel, unit) format -> 'a = ~~~ ~~~ # format_of_string "" ;; - : ('_a, '_b, '_c, '_d, '_d, '_a) format6 = CamlinternalFormatBasics.Format (CamlinternalFormatBasics.End_of_format, "") ~~~ ~~~ # format_of_string "%d" ;; - : (int -> '_a, '_b, '_c, '_d, '_d, '_a) format6 = CamlinternalFormatBasics.Format (CamlinternalFormatBasics.Int (CamlinternalFormatBasics.Int_d, CamlinternalFormatBasics.No_padding, CamlinternalFormatBasics.No_precision, CamlinternalFormatBasics.End_of_format), "%d") ~~~ ~~~ # format_of_string "%d%x" ;; - : (int -> int -> '_a, '_b, '_c, '_d, '_d, '_a) format6 = CamlinternalFormatBasics.Format (CamlinternalFormatBasics.Int (CamlinternalFormatBasics.Int_d, CamlinternalFormatBasics.No_padding, CamlinternalFormatBasics.No_precision, CamlinternalFormatBasics.Int (CamlinternalFormatBasics.Int_x, CamlinternalFormatBasics.No_padding, CamlinternalFormatBasics.No_precision, CamlinternalFormatBasics.End_of_format)), "%d%x") ~~~ ~~~ # format_of_string "foo%sfoo";; - : (bytes -> '_a, '_b, '_c, '_d, '_d, '_a) format6 = CamlinternalFormatBasics.Format (CamlinternalFormatBasics.String_literal ("foo", CamlinternalFormatBasics.String (CamlinternalFormatBasics.No_padding, CamlinternalFormatBasics.String_literal ("foo", CamlinternalFormatBasics.End_of_format))), "foo%sfoo") ~~~ ### num library / str library / labltk library num / str / labltk libraries are hidden libraries. ~~~ # #load "nums.cma" ;; # open Num ;; # (+/) (Int 1) (Int 1) ;; // Int # let a = (Int 4) // (Int 8) ;; // Ratio # string_of_num a ;; // "1/2" ~~~ ~~~ # #load "str.cma" # #show Str :: ... ~~~ # lablgtk ~~~ $ opam install lablgtk # #load "l" ~~~ # compile src files ~~~ (* fact.ml *) let rec fact n = if n = 0 then 1 else n * fact ( n - 1 ) (* main.ml *) open Fact let () = print_int (fact 10); print_newline () ~~~ then compile ~~~ $ ocamlc -o fact10 fact.ml main.ml ~~~ or ~~~ $ ocamlc -c fact.ml $ ocamlc -c main.ml $ ocamlc -o fact10 fact.cmo main.cmo ~~~ this is equal on utop interpreter ~~~ > module Fact = struct (* fact.ml *) end > module Main = struct (* main.ml *) end;; ~~~ ### `-i` option show types of functions ~~~ > ocamlc -i -c fact.ml val fact : int -> int ~~~ ### read object file ~~~ $ ocaml fact.cmo OCaml version 4.02.3 > Fact.fact 10;; - : int = 3628800 ~~~ ### `cma` file now assume foo.ml using `nums.cma` ~~~ $ ocamlc -c foo.ml $ ocamlc -o foo nums.cma foo.cmo ~~~ # batch compiler ~~~ batch : 1. a number of people or things that are dealt as a group He worked his way through the batch of letters on his desk Each summer a new batch of students tries to find work. We deliver the goods in batches. batch : an amount of food, machine , etc. produced at one time a batch of cookies loaves of bread baked in bathes of 20 batch : (computing) a set of jobs that are processed together on computer a batch file a batch program ~~~ 1. generate 'object files' 2. link 'object files' into one 'executable file' ocamlc - generate 'byte code' : non-dependent on OS / machines - run on OCaml VM ocamlopt - generate 'machine code' : dependent on OS / machines - run on OS/Machine ## errors unique at batch compiler Suppose compiling a file with a single line like this; ~~~ let x = ref [] ~~~ This value x do not have a type.
So it turned out error at batch compilation. ## options ocamlc command option ~~~ -c : generate object file which is unliked yet -dtypes : write type information into '.annot' file which is used by 'ocaml-mode' see Apendix C -i : informing types without compiling -I dirName : serach the directory for '.cmi' and '.cmo' -I +dirName : '+' denotes the path of Standard Library -o : specify the name of output executable file ~~~ ## batch compile hello.ml ~~~ let () = print_string "Hello, World!\n" ~~~ ### ocamlc ~~~ $ ocamlc -o hello hello.ml $ ./hello Hello, World! ~~~ ### ocamlopt ~~~ $ ocamlopt -o hello hello.ml $ ls hello.ml hello.cmi hello.cmx hello.o hello $ ./hello Hello, World! ~~~ ## seperate compile hello.ml ~~~ let hello = print_string "Hello, World!\n" ~~~ main.ml ~~~ open Hello let () = hello; ~~~ ### ocamlc (generates .cmo file ) ~~~ $ ocamlc -c hello.ml $ ocamlc -c main.ml $ ocamlc -o hello hello.cmo main.cmo // DO NOT SWAP the order ~~~ ### ocamlopt (generates .cmx file) ~~~ $ ocamlopt -c hello.ml $ ocamlopt -c main.ml $ ocamlopt -o hello hello.cmx main.cmx ~~~ ## informing types ~~~ $ ocamlc -i -c hello.ml // just inform the types of functions val hello : unit ~~~ ## interpretor with 'object file' ~~~ $ ls hello.cmi hello.cmo hello.ml $ ocaml hello.cmo // this process also need the information of the types 'hello.cmi' # Hello.hello;; - : unit = () ~~~ ## cma file ~~~ $ ocamlc -o hello hello.cmo main.cmo ~~~ is indeed ~~~ $ ocamlc -o hello stdlib.cma hello.cmo main.cmo ~~~ but this `stdlib.cma` is compiled implicitly. if you use `Num` module in foo.ml include `nums.cma`; ~~~ $ ocamlc -c foo.ml $ ocamlc -o foo nums.cma foo.ml ~~~ ## mli file / cmi file ~~~ $ ocamlc table.mli // generate table.cmi $ ocamlc -c table.ml $ ocamlc -c tablemain.ml $ ocamlc -o tablemain (stdlib.cma) table.cmo tablemain.cmo ~~~ ~~~ if '.mli' file does not exist, then compiler makes '.cmi' file automatically with guessing the types if '.mli' file exist then compiler does not make '.cmi' file automatically. so if you didn`t compile '.mli' first, '.cmi' file never exist, thereby cannot compile '.cmo' file. ~~~ that is ~~~ '.cmi' depends on '.mli' '.cmo' depends on '.cmi' 'execution file' depends on all '.cmo' ~~~ ## `ocamldep` it automalically generate the dependency relation with Makefile !! ~~~ $ ocamldep ## Make 'byte code' $ ocamldep -native ## Make 'machine code' see chap 13 ~~~ # Modules ## Sys ~~~ # Sys.argv.(0) ;; ~/.opam/system/bin/utop # Sys.getenv "HOME" ;; /Users/yourname # Sys.file_exists "hoge" - : bool = false # Sys.remove - : string -> unit = # Sys.rename - : string -> string -> unit = ~~~ ## Arg ~~~ type specs = (key * spec * doc) list ~~~ ~~~ type spec = Unit of (unit -> unit) | Set of bool ref | Clear of bool ref | String of (string -> unit) | Int of (int -> unit) | Float of (float -> unit) | Rest of (string -> unit) ~~~ ~~~ let files = ref [] parse specs (fun str -> files := str :: !files) ~~~ The second argument of parse function is how to deal with the `Rest` arguments. parse function automatically generate `-help` spec. ## the same program on interpreter ~~~ module Hello = struct (hello.ml) end module Main = struct (main.ml) end;; ~~~