OCaml

wiki https://en.wikipedia.org/wiki/OCaml
manual https://ocaml.org/manual/index.html
manual (04) https://caml.inria.fr/pub/old_caml_site/ocaml/htmlman/index.html

Language

  • there are mutable values, but NOT mutable variables
  • .ml = Data Structure
  • .mli = Data Abstraction
  • let () =, takes the role of the main function
  • The let definition is not an expression in itself
  • The let in is an expression

Types

  • best read from right to left

    int list list (* "a list of list of integers" *)
    

record

type [typeparam] <record-name> =
  { <field> : <type-or-typeparam>;
    <field> : <type>;
    ...
  }

with aka functional update of field(s), CANNOT add new fields (?

{ e with f1 = e1 }

destructuring

type service_info = { service_name : string ; port : int ; protocol : string }
let service_info_to_string { service_name = name; port = port; protocol = prot } =
  sprintf "%s %i/%s" name port prot

Fields punning is when the name of a variable coincides with the name of a record field. Works both for destructuring and constructing.

let service_info_to_string { service_name ; port ; protocol} =
  sprintf "%s %i/%s" service_name port protocol

a default accessor function is created for each record field, different records having the same name for a field might be a problem and confuse the type inference. A solution can wrap each records in different modules.

(* defining *)
module Log_entry = struct
  type t =
    { session_id : string
    ; time       : Time_ns.t
    ; important  : bool
    ; message    : string
    }
end
(* instantiation *)
let create_log_entry ~session_id ~important message =
  { Log_entry.
    time = Time_ns.now (); session_id; important; message;
  }
let create_log_entry ~session_id ~important message : Log_entry.t = (* signature *)
  { time = Time_ns.now (); session_id; important; message; }
(* pattern matching / destructuring *)
let message_to_string { Log_entry.important; message; _ } = (* module qualification *)
  if important then String.uppercase message else message
let message_to_string ({ important; message; _} : Log_entry.t) = (* type annotation *)
  if important then String.uppercase message else message
(* field access *)
let is_important t = t.Log_entry.important (* module qualification *)
let is_important (t:Log_entry.t) = t.important (* type annotation *)

variant (sum type)

type <variant> =
  | <TagOrConstructor> [of <type> [* <type>]...]
  | <TagOrConstructor> [of <type> [* <type>]...]
  | ...

We can also use the type name when matching for variant types, in this case "color" is the type name

let extended_color_to_int : extended_color -> int = function
  | `RGBA (r,g,b,a) -> 256 + a + (b * 6) + (g * 36) + (r * 216)
  | #color as color -> color_to_int color

simple

  • Int
  • Float
  • String

complex

  • List []
    • single linked list
    • List.Assoc, module for associative lists [(aka list; of tuples)]
Type Init Access Modifying Pattern Matching Adding Appending
Unit ()          
Bool x = true,false       && ¦¦  
Option Some a, None          
String "abcd" .[2] <-     ^ String.concat
mutable     <-      
refs ref <expr> !x :=      
Array [¦ a ; b ¦] .(2) <- let [¦ a ; b ¦] = some_array    
Bigarray   .{2} <-      
tuples (a , b) fst snd   let (x , y) =    
List [a ; a] List.hd   let (x :: y) = :: @ List.append
struct struct let foo = 3 end          
record {a = VAL ; b = VAL} .a   let { x = xpos ; y = ypos } =    
  a=VAL; B=VAL; {a ; b}     let { x ; y } =    
  type foo = { a: int }     let v1 = v1.x + v2.y    

Modules

  • can be open locally

    let average x y = let open Int64 in (x + y) / of_int 2
    let average x y = Int64((x + y) / of_int 2)
    
  • local let module shortcut

    let print_median m =
      let module C = Counter in
      match m with
      | C.Median string -> printf "%s" string
      | C.Before_and_after (before, after) -> printf "%s %s\n" before after
    
  • new type

    module type ID = sig
      type t
      val of_string : string -> t
    end
    
  • new struct

    module String_id = struct
      type t = string
      let of_string x = x
    end
    
  • join?? type+struct into one type

    module M : sig
      type t
    end = struct
      type t = string
    end
    
  • join?? type+struct into a several types

    module Username : ID = String_id
    module Hostname : ID = String_id
    
  • include, to extend a "class" adding new values on a new module type

    module newM = struct
      include otherM
      let f x = m
    end
    
new functor module fM (M:MT) : MT = struct
  ..let x = M.x + 1
  ..let f i = M.g i
  end
instance/use functor module newM = functorM(M)
instance functor module newM =
+ ..functorM(struct
anonymous module ..type t = int
  ..let compare = Int.compare
  end)
include with type module newM = sig
(multiple) ..type t
  ..include M1 with type t := t
  ..include M2 with type t := t
  end
new module with type module type MT =
"sharing constraint" ..MT with type T1 = T2
new functor with type module fM(M:MT1)
"sharing constraint" ..: (MT2 with type T1 = M.t)
  = struct
  ..type T1 = M.t
  end
new module with type := module type MT =
"destructive substitution" ..MT with type T1 := T2
new functor with type := module fM(M:MT1)
"destructive substitution" ..: MT2 with type T1 := T2 =
  struct
  end

Style

Functions

let plusone x = x + 1
let plusone = (fun x -> x + 1) (* lambda *)
let plusone = ( + ) 1 (* partial application *)

signatures (.mli)

open Base
type t
val empty : t
val to_list : t -> (string * int) list
type touch : t -> string -> t

one of the possible implementations for that signature

open Base
type t = (string * int) list
let empty = []
let to_list x = x
let touch counts line =
  let count =
    match List.Assoc.find ~equal:String.equal counts line with
    | None   -> 0
    | Some x -> x
  in
  List.Assoc.add ~equal:String.equal counts line (count + 1)

rec + function + as + when (guard)

let rec destutter list = function
  | [] | [_] as l -> l
  | hd :: (hd' :: _ as tl) when hd = hd' -> destutter tl
  | hd :: tl -> hd :: destutter tl

Function parameters are patterns too

type point = float * float

let distance ((x1,y1):point) ((x2,y2):point) : float =
  let square x = x *. x in
  sqrt (square (x -. x1) +. square (y2 -. y1))
  • _exn denotes functions that return an exception
  • assert false can be used on a branch that isn't suppose to happen
  • optional type annotation
  • type variables support for parametric polymorphism
  • implicit match of an invisible last argument when using function
  • lexically scoped (with let)
  • optional arguments
    • begin with ?
      • ?sep ?(sep="")
      • ~sep:":"
    • passed as Option
    • useful when is a wrapper to another function that takes an optional
  • labeled arguments (ME: aka keyword args)
    • at any position of arguments
    • defining: ~num
    • passing arguments
      • ~num:3
      • ~num - with label punning if a variable with the same name exists
      • ~f:String:Length
    • when is unclear just from the type signature
      • booleans
      • more than one value of the same type

Operators

  • Declaring an infix operator

    let ( <^> ) x y = max x y
        in 1 <^> 2
    
  • default polymorphic operators considered problematic
  • String.concat vs ^
    • .concat allocates 1 string
    • ^ allocates a new string every time it runs
  • ( * ) is preferred over (*) for writting operators as functions
^ concatenate strings
@@ application (like $ in Haskell)
¦> pipeline, left assoc (aka rev application)
^> pipeline, right assoc
== physical equality
!= physical inequality
= structural equality (aka contents), or assignment
<> structural inequality
>>= Option.bind
>>¦ Option.map
; to sequence imperative code
[] "list"
:: "cons"

Standard Library

https://v2.ocaml.org/manual/stdlib.html

Module Description
Arg parsing of command line arguments
Array array operations
ArrayLabels array operations (with labels)
Atomic atomic references
Bigarray large, multi-dimensional, numerical arrays
Bool boolean values
Buffer extensible buffers
Bytes byte sequences
BytesLabels byte sequences (with labels)
Callback registering OCaml values with the C runtime
Char character operations
Complex complex numbers
Condition condition variables to synchronize between threads
Domain Domain spawn/join and domain local variables
Digest MD5 message digest
Effect deep and shallow effect handlers
Either either values
Ephemeron Ephemerons and weak hash table
Filename operations on file names
Float floating-point numbers
Format pretty printing
Fun function values
Gc memory management control and statistics; finalized values
Hashtbl hash tables and hash functions
In_channel input channels
Int integers
Int32 32-bit integers
Int64 64-bit integers
Lazy deferred computations
Lexing the run-time library for lexers generated by ocamllex
List list operations
ListLabels list operations (with labels)
Map association tables over ordered types
Marshal marshaling of data structures
MoreLabels include modules Hashtbl, Map and Set with labels
Mutex locks for mutual exclusion
Nativeint processor-native integers
Oo object-oriented extension
Option option values
Out_channel output channels
Parsing the run-time library for parsers generated by ocamlyacc
Printexc facilities for printing exceptions
Printf formatting printing functions
Queue first-in first-out queues
Random pseudo-random number generator (PRNG)
Result result values
Runtime_events Runtime event tracing
Scanf formatted input functions
Seq functional iterators
Set sets over ordered types
Semaphore semaphores, another thread synchronization mechanism
Stack last-in first-out stacks
StdLabels include modules Array, List and String with labels
String string operations
StringLabels string operations (with labels)
Sys system interface
Uchar Unicode characters
Unit unit values
Weak arrays of weak pointers

Codebases


Created: 2020-11-24

Updated: 2024-11-16

Back