General
Character constants:
'a'List concatenation:
l1 @ l2String concatenation:
str1 ^ str2orString.cat str1 str2Format string concatenation:
str1 ^^ str2Anonymous function:
fun x y -> x + y@@: comparable to$in haskell. First evaluates RHS then applies LHS to it. ʳ'~Module.(e)~ is equivalent to
let open Module in e' ʳ- Open a module temporarily for an expression.
Check installed modules:
ocamlfind listAnonymous function:
fun x -> 3difference between = and ==:
==: physical equality=: structural equality- https://www2.lib.uchicago.edu/keith/ocaml-class/operators.html
Integer division:
/Float division:
/.else if, notelseif
Function arguments are eagerly evaluated.
Variables are made with
let.Recursive functions are made with
let rec.ocamlfmt: an automatic code reformatter for ocaml.mlifiles: types for definitions in corresponding .ml file.cmifiles: compiled form of .mli used during compilation?Useful: https://doc.sherlocode.com/
- Like hoogle (haskell)
More built-in modules:
- Like Array, but length can vary over time: https://ocaml.org/manual/5.2/api/Dynarray.html
Partial application of function
# (=) 3 2;;
- : bool = false
# (=) 3 3;;
- : bool = trueList
mem x l: x ∈l
https://ocaml.org/manual/5.2/api/List.html
Flip function: Fun.flip
# Fun.flip;;
- : ('a -> 'b -> 'c) -> 'b -> 'a -> 'c = <fun>
# List.mem;;
- : 'a -> 'a list -> bool = <fun>
# Fun.flip List.mem;;
- : '_weak1 list -> '_weak1 -> bool = <fun>
# Fun.flip List.mem [2;3];;
- : int -> bool = <fun>
# Fun.flip List.mem [2;3] 4;;
- : bool = false
# Fun.flip List.mem [2;3] 3;;
- : bool = trueOptional arguments
Like Labeled arguments, but with a default value and ? instead of a tilde.
>>> let foo ?(a=3) b = a + b;;
val foo : ?a:int -> int -> int = <fun>
>>> foo 2;;
- : int = 5
(* Overriding default value *)
>>> foo ~a:10 2;;
- : int = 12
(* Yeah, order doesn't matter *)
>>> foo 2 ~a:10;;
- : int = 12
>>> foo ~a:10;;
- : int -> int = <fun>
Reference: https://v2.ocaml.org/manual/lablexamples.html#s:optional-arguments
(|>) operator
It's like a reversed apply. ʳ
Function can be made to appear on RHS and its argument in LHS.
(|>);;
- : 'a -> ('a -> 'b) -> 'b = <fun>
0.0 |> sin;;
- : float = 0.
sin 0.0;;
- : float = 0.Useful to make a 'pipeline' where the value flows from the left to the right. As in:
let simple_process s =
Str.split Owl_nlp_utils.regexp_split s
|> List.filter (fun x -> String.length x > 1)
|> String.concat " "
|> String.lowercase_ascii
|> Bytes.of_string(forgot from where I got this snippet..)
Empty type
We can make 'empty types'. ie, types without even a single constructor. ¹
Like:
type empty = |Lack of constructors means it's impossible to construct a value of type empty.
Explicit subtyping info in type variables
Via variance annotations.
- Covariant:
+'a - Contravariant:
-'a
By default, type variables are covariant.
ocaml infers them, but the explicit notation can be helpful in the 'interface' of modules (ocaml analogue of structures).
References:
Reference (variables)
Mutable values.
# incr;;
- : int ref -> unit = <fun>
# let r = ref 0;;
val r : int ref = {contents = 0}
# !r;;
- : int = 0
# incr r;;
- : unit = ()
# !r;;
- : int = 1
Record types
type person = {
name: string;
age: int
}mutable keyword in ocaml
Modifiable values. ie, non-pure.
mutable is not part of the type. Just property of the variable.
type point = {
x: int;
mutable y: int
}
# let p = {x=3; y=4};;
val p : point = {x = 3; y = 4}
# p.x <- 10
Error: The record field x is not mutable
# p.y <- 10;;
- : unit = ()
# p
- : point = {x = 3; y = 10}References:
- https://caml.inria.fr/pub/docs/fpcl/fpcl-07.pdf
- https://www.cs.cornell.edu/courses/cs3110/2018sp/l/14-mutable/notes.html
- https://cs3110.github.io/textbook/chapters/mut/mutable_fields.html
Tilde in arguments
They are labelled arguments.
let rec range ~first:a ~last:b =
if a > b then []
else a :: range ~first:(a+1) ~last:b
# range
- : first:int -> last:int -> int list = <fun>
(* Show the argument labels! *)
# range 3 5
- : int list = [3; 4; 5]
(* Now argument order can be changed *)
# range ~last:10 ~first:3;;
- : int list = [3; 4; 5; 6; 7; 8; 9; 10]Multi-line strings
Use within {| and |}.
# {| hello
world
|};;
- : string = " hello\n world\n "pattern matching
See: https://v2.ocaml.org/manual/patterns.html
Can use function to pattern match without actually naming variables:
(* Given two lists, check if their concatenation is nil *)
let is2Nil = function
| [], [] -> true
| _, _ -> false
(* is2Nil : 'a list * 'b list -> bool *)Or can use match as in Coq:
let is2Nil a b =
match a, b with
| [], [] -> true
| _, _ -> falseArray
- Array is mutable.
https://ocaml.org/manual/5.2/api/Array.html
- Indexing
- 1-D array:
arr.(i)/get arr i - 2-D array:
arr.(i).(j)
- 1-D array:
- Writing:
arr.(i) <- new_val- Or
set arr i new_val
- Or
- Length:
length arr
# let a = [|1; 2; 3; 4|];;
val a : int array = [|1; 2; 3; 4|]sml vs ocaml
Analogues:
| sml | ocaml | comment |
|---|---|---|
| structure | module | |
| signature | module type | |
| fn | fun | Anonymous functions |
| f o g | -NA- | Function composition |
| case | match | Pattern matching |
Apparently, ocaml doesn't have a built-in compose function.
—
List elements are separated with ; in ocaml and , in sml
sml:
Standard ML of New Jersey (64-bit) v110.95 [built: Sat Feb 05 19:22:43 2022]
[1,2,3];;
val it = [1,2,3] : int listOCaml:
OCaml version 4.14.1
Enter #help;; for help.
[1,2,3];;
- : (int * int * int) list = [(1, 2, 3)]
[1;2;3];;
- : int list = [1; 2; 3]See: http://adam.chlipala.net/mlcomp/
Imperative features
From https://ocaml.org/docs/objects:
OCaml is an object-oriented, imperative, functional programming language. It mixes all these paradigms and lets you use the most appropriate (or most familiar) programming paradigm for the task at hand.
(Haskell is 'purer' than OCaml in the sense that there is lesser chance of side-effects.)
OOP (Object-oriented programming)
ocaml supports OOP-style classes: https://ocaml.org/docs/objects
- Access method
fooof an objectobjwithobj#foo newkeyword is used to make a new object of a class- Eg:
new LTerm_widget.hbox
- Eg:
Loops
https://ocaml.org/docs/if-statements-and-loops
- Both
forandwhileloops available. - Can't break out of loop early: No
break,continue
Doubts
- Use of
.mlifiles - Camlp4 and Camlp5: for writing (complex?) parsers
Infix operator as prefix
Enclose the operator within parenthesis.
Example:
utop # List.map (fun s -> "prefix " ^ s) l;;
- : string list = ["prefix hi"; "prefix hello"]
utop # List.map ((^) "prefix ") l;;
- : string list = ["prefix hi"; "prefix hello"]
utop # List.map (String.cat "prefix ") l;;
- : string list = ["prefix hi"; "prefix hello"]
utop # (^) ;;
- : string -> string -> string = <fun>
utop # (^) "a";;
- : string -> string = <fun>Making an infix operator/function
Just put the name consisting of special characters and enclose it within parenthesis, I guess. https://stackoverflow.com/questions/38147841/how-to-define-an-infix-not-symbolic-aka-not-an-operator-function-in-ocaml
utop # let (+-) x y = x + y;;
val ( +- ) : int -> int -> int = <fun>
utop # 3 +- 4;;
- : int = 7
utop # let (a) x y = x + y;;
Error: Syntax error
if without else
This is possible if the then part value is of type unit.
fun x -> if x<3 then () ;;
- : int -> unit = <fun>Otherwise, it would give error:
# fun x -> if x<3 then "hi" ;;
Error: This expression has type string but an expression was expected of type
unit
because it is in the result of a conditional with no else branchLinks:
- https://v2.ocaml.org/manual/expr.html#sss:expr-conditional
- https://stackoverflow.com/questions/19338709/ocaml-is-it-possible-to-creat-single-if-without-else
Function vs value
This is a function:
let gethm () =
let now = Unix.localtime (Unix.time ()) in
let hour = now.tm_hour in
let minute = now.tm_min in
(hour, minute)But this is a value:
let gethm =
let now = Unix.localtime (Unix.time ()) in
let hour = now.tm_hour in
let minute = now.tm_min in
(hour, minute)Some errors
#load
Error: Reference to undefined global `Unix' Hint: This means that the interface of a module is loaded, but its implementation is not. Found home/famubu.opam/5.1.0/lib/ocaml/unix/unix.cma in the load paths. Did you mean to load it using #load "unix.cma" or by passing it as an argument to the toplevel?
let now = Unix.localtime (Unix.time ()) in let hour = now.tmhour in let minute = now.tmmin in (hour, minute);;
val gethm : int * int = (11, 25)
#+endsrc
Lack of information on exception
$ dune exec clock
2024-08-19 09:08:43.72231Z ERROR [thread=3] Process <0.5.0> died with unhandled exception Invalid_argument("index out of bounds"):
$ OCAMLRUNPARAM=b
$ dune exec clock
2024-08-19 09:09:35.34246Z ERROR [thread=0] Process <0.6.0> died with unhandled exception Invalid_argument("index out of bounds"):
Raised by primitive operation at Clock__Model.build_outstr in file "lib/model.ml", line 112, characters 8-24
Called from Minttea__Program.init in file "minttea/program.ml", line 49, characters 13-35
Called from Runtime__Import._spawn.(fun) in file "riot/runtime/import.ml", line 92, characters 10-15Another example:
$ export OCAMLRUNPARAM=b
$ dune exec clock
2024-08-19 09:11:36.12397Z ERROR [thread=2] Process <0.6.0> died with unhandled exception Invalid_argument("index out of bounds"):
Raised by primitive operation at Clock__Model.build_outstr in file "lib/model.ml", line 79, characters 8-22
Called from Minttea__Program.init in file "minttea/program.ml", line 49, characters 13-35
Called from Runtime__Import._spawn.(fun) in file "riot/runtime/import.ml", line 92, characters 10-15