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;;
~~~