OCaml


General

Partial application of function

# (=) 3 2;;
- : bool = false

# (=) 3 3;;
- : bool = true

List

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 = 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.

(|>);;
- : '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.

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:

Tilde in arguments

They are labelled arguments.

https://ocaml.org/docs/labels

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
  | _, _ -> false

Array

https://ocaml.org/manual/5.2/api/Array.html

# 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 list

OCaml:

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

Loops

https://ocaml.org/docs/if-statements-and-loops

Doubts

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 branch

Links:

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