General
Character constants:
'a'
List concatenation:
l1 @ l2
String concatenation:
str1 ^ str2
orString.cat str1 str2
Format string concatenation:
str1 ^^ str2
Anonymous 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 list
Anonymous function:
fun x -> 3
difference 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.mli
files: types for definitions in corresponding .ml file.cmi
files: 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 = true - :
List
mem x l
: x ∈l
https://ocaml.org/manual/5.2/api/List.html
Flip function: Fun.flip
# Fun.flip;;fun>
- : ('a -> 'b -> 'c) -> 'b -> 'a -> 'c = <
List.mem;;
# list -> bool = <fun>
- : 'a -> 'a
List.mem;;
# Fun.flip list -> '_weak1 -> bool = <fun>
- : '_weak1
List.mem [2;3];;
# Fun.flip int -> bool = <fun>
- :
List.mem [2;3] 4;;
# Fun.flip bool = false
- :
List.mem [2;3] 3;;
# Fun.flip bool = true - :
Optional 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.
(|>);;fun>
- : 'a -> ('a -> 'b) -> 'b = <
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 sList.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 = {
string;
name: int
age: }
mutable
keyword in ocaml
Modifiable values. ie, non-pure.
mutable
is not part of the type. Just property of the variable.
type point = {
int;
x: mutable y: int
}
let p = {x=3; y=4};;
# val p : point = {x = 3; y = 4}
10
# p.x <- not mutable
Error: The record field x is
10;;
# p.y <- unit = ()
- :
# p3; y = 10} - : point = {x =
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
# rangeint -> last:int -> int list = <fun>
- : first:(* Show the argument labels! *)
3 5
# range int list = [3; 4; 5]
- :
(* Now argument order can be changed *)
10 ~first:3;;
# range ~last: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
| [], [] -> false | _, _ ->
Array
- 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:
of New Jersey (64-bit) v110.95 [built: Sat Feb 05 19:22:43 2022]
Standard ML 1,2,3];;
[val it = [1,2,3] : int list
OCaml:
4.14.1
OCaml version for help.
Enter #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
foo
of an objectobj
withobj#foo
new
keyword 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
for
andwhile
loops available. - Can't break out of loop early: No
break
,continue
Doubts
- Use of
.mli
files - Camlp4 and Camlp5: for writing (complex?) parsers
Infix operator as prefix
Enclose the operator within parenthesis.
Example:
List.map (fun s -> "prefix " ^ s) l;;
utop # string list = ["prefix hi"; "prefix hello"]
- :
List.map ((^) "prefix ") l;;
utop # string list = ["prefix hi"; "prefix hello"]
- :
List.map (String.cat "prefix ") l;;
utop # string list = ["prefix hi"; "prefix hello"]
- :
utop # (^) ;;string -> string -> string = <fun>
- :
"a";;
utop # (^) 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" ;;
# type string but an expression was expected of type
Error: This expression has unit
in the result of a conditional with no else branch because it is
Links:
- 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-15
Another 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