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 |
- 1996-
- Authors:
- Xavier Leroy, Jerome Voiullon, Damien Doligez, Didier Remy, Ascander Suarez
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
locallylet 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
- guide https://www.cs.cornell.edu/courses/cs3110/2016fa/handouts/style.html
- Modules
- variables, functions, recordtypes
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 usingfunction
- 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
- begin with ?
- 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 |