Skip to content

Instantly share code, notes, and snippets.

@jzelinskie
Last active December 29, 2015 03:48
Show Gist options
  • Save jzelinskie/7609842 to your computer and use it in GitHub Desktop.
Save jzelinskie/7609842 to your computer and use it in GitHub Desktop.
OCaml Gotchas

a list of OCaml complexities extracted from Real World OCaml

arrays are by default mutable (and the syntax is crazy)

let array = [| "this";"is";"a";"array"; |];;
(* val array : string array = [|"this"; "is"; "a"; "array"|] *)

(** accessing a value *)
array.(2);;
(* - : string = "a" *)

(** setting a value returns unit; basically "void" in other languages *)
array.(2) <- "an";;
(* - : unit = () *)

array;;
(* - : string array = [|"this"; "is"; "an"; "array"|] *)

^ concatonates strings

(** at least it's not php! *)
let derpherp = "herp" ^ "derp";;
(* val herpderp : string = "herpderp" *)

chained ^ allocate many new strings; String.concat allocates one

(** allocates strings of length 2, 3, 4, 5, 6 and 7 *)
let s = "." ^ "."  ^ "."  ^ "."  ^ "."  ^ "."  ^ ".";;
(* val s : string = "......." *)

(** allocates one string of length 7 *)
let s = String.concat [".";".";".";".";".";".";"."];;
(* val s : string = "......." *)

variables must start with [a-z] or _

(** they can also contain [A-Z], [0-9], and ' *)
let x7 = 3 + 4;;
(* val x7 : int = 7 *)

let x_plus_y = x + y;;
(* val x_plus_y : int = 21 *)

(** utop doesn't print vals starting with _ *)
let _dErp' = 8;;
(* val _dErp' : int = 8 *)

let Seven = 3 + 4;;
(* Characters 4-9:
Error: Unbound constructor Seven *)

let 7x = 7;;
(* Characters 5-10:
Error: This expression should not be a function, the expected type is int *)

let x-plus-y = x + y;;
(* Characters 4-5:
Error: Parse error: [fun_binding] expected after [ipatt] (in [let_binding]) *)

values can be shadowed in let expressions

let area_of_ring inner_radius outer_radius =
     let pi = acos (-1.) in
     let area_of_circle r = pi *. r *. r in
     let pi = 0. in (** pi changed, but never used after shadowing *)
     area_of_circle outer_radius -. area_of_circle inner_radius
  ;;
(* Characters 126-128:
Warning 26: unused variable pi.val area_of_ring : float -> float -> float = <fun> *)

defining new operators requires parenthesis

let (+!) (x1,y1) (x2,y2) = (x1 + x2, y1 + y2);;
(* val ( +! ) : int * int -> int * int -> int * int = <fun> *)

(** be careful: comments also use parenthesis! *)
let (***) x y = (x ** y) ** y;;
(* Characters 17-18:
Error: This expression has type int but an expression was expected of type
         float *)

let ( *** ) x y = (x ** y) ** y;;
(* val ( *** ) : float -> float -> float = <fun> *)

the first character in an operator says a lot about it

Refer to this table and paragraphs before and after the table.

function application has a higher precedence than negation

Int.max 3 (-4);;
(* - : int = 3 *)

Int.max 3 -4;;
(* Characters -1-9:
Error: This expression has type int -> int
       but an expression was expected of type int *)

labels need to be consistently ordered in HOFs

let apply_to_tuple f (first,second) = f ~first ~second;;
(* val apply_to_tuple : (first:'a -> second:'b -> 'c) -> 'a * 'b -> 'c = <fun> *)

let apply_to_tuple_2 f (first,second) = f ~second ~first;;
(* val apply_to_tuple_2 : (second:'a -> first:'b -> 'c) -> 'b * 'a -> 'c = <fun> *)

let divide ~first ~second = first / second;;
(* val divide : first:int -> second:int -> int = <fun> *)

apply_to_tuple_2 divide (3,4);;
(* Characters 17-23:
Error: This expression has type first:int -> second:int -> int
       but an expression was expected of type second:'a -> first:'b -> 'c *)

let apply_to_tuple f (first,second) = f ~first ~second;;
(* val apply_to_tuple : (first:'a -> second:'b -> 'c) -> 'a * 'b -> 'c = <fun> *)

apply_to_tuple divide (3,4);;
(* - : int = 0 *)

use ?arg for wrappers around functions with optional args

let concat ?(sep="") x y = x ^ sep ^ y ;;
(* val concat : ?sep:string -> string -> string -> string = <fun> *)

(** will have to be refactored if concat changes *)
let uppercase_concat ?(sep="") a b = concat ~sep (String.uppercase a) b ;;
(* val uppercase_concat : ?sep:string -> string -> string -> string = <fun> *)

(** won't need refactoring *)
let uppercase_concat ?sep a b = concat ?sep (String.uppercase a) b ;;
(* val uppercase_concat : ?sep:string -> string -> string -> string = <fun> *)

partial application can erase an optional arg

The rule is: an optional argument is erased as soon as the first positional (i.e., neither labeled nor optional) argument defined after the optional argument is passed in.

the OCaml debugger only works with bytecode

You have to use gdb when you compile to a native binary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment