SML and OCaml, Side by Side
Disclaimer
This is dump/copy of Standard ML and Objective Caml, Side by Side
Original version by Jens Olsson, jenso@csd.uu.se.
Last modified: 2011/01/18
You may ask “Why making copy instead of simple references to the source?” -
Because, I’d like to have it as is without looking around internet when ppl decide to delete it.
This page gives a quick side by side comparison of program fragments in the two ML dialects Standard ML (‘97 revision) and Objective Caml (version 3.12). It is primarily targetted at people who need to convert code between the two dialects. Where suitable we also mention common extensions to SML, or recent extensions of Ocaml. The comparison does not cover features that do not have an appropriate counter part in the sibling dialect (e.g. Ocaml’s object sublanguage, SML’s user-defined operator fixity, or advanced library issues).
Literals
| SML | Ocaml |
- 3; > val it = 3 : int | # 3;; - : int = 3 |
- 3.141; > val it = 3.141 : real | # 3.141;; - : float = 3.141 |
- "Hello world"; > val it = "Hello world" : string | # "Hello world";; - : string = "Hello world" |
- #"J"; > val it = #"J" : char | # 'J';; - : char = 'J' |
- true; > val it = true : bool | # true;; - : bool = true |
- (); > val it = () : unit | # ();; - : unit = () |
- (3, true, "hi"); > val it = (3, true, "hi") : int * bool * string | # (3, true, "hi");; - : int * bool * string = 3, true, "hi" |
- [1, 2, 3]; > val it = [1, 2, 3] : int list | # [1; 2; 3];; - : int list = [1; 2; 3] |
- #[1, 2, 3]; > val it = #[1, 2, 3] : int vector Standard does not have vector literals but most implementations support them – use library functions otherwise | Does not have vectors – use arrays |
| Does not have array literals – use library functions | # [|1; 2; 3|];; - : int array = [|1; 2; 3|] |
Expressions
| SML | Ocaml |
~3*(1+7) div 2 mod 3 | -3*(1+7)/2 mod 3 |
~1.0/2.0 + 1.9*x | -1.0 /. 2.0 +. 1.9 *. x |
a orelse b andalso c | a || b && c or (deprecated) a or b & c |
Functions
| SML | Ocaml |
fn f => fn x => fn y => f(x,y) | fun f -> fun x -> fun y -> f (x,y) or fun f x y -> f (x,y) |
fn 0 => 0 | n => 1 | function 0 -> 0 | n -> 1 |
f o g | fun x -> f (g x) |
map SOME xs | Does not have first-class constructors – use function instead, e.g. map (fun x -> Some x) xs |
map #2 triples map #lab records | Does not have first-class selectors – use function instead, e.g. map (fun (_,x,_) -> x) triples map (fun x -> x.lab) records |
f (inputLine stdIn) (inputLine stdIn) | Evaluation order is undefined for application – use let, e.g. let line1 = read_line () in let line2 = read_line () in f line1 line2 |
Control Flow
| SML | Ocaml |
if 3 > 2 then "X" else "Y" | if 3 > 2 then "X" else "Y" |
if 3 > 2 then print "hello" else () | if 3 > 2 then print_string "hello" Note: expression has to have type unit |
while true do print "X" | while true do print_string "X" done |
Does not have for loops – use recursion or while | for i = 1 to 10 do print_endline "Hello" done |
(print "Hello "; print "world") | print_string "Hello "; print_string "world" or (print_string "Hello "; print_string "world") or begin print_string "Hello "; print_string "world" end |
Value Declarations
| SML | Ocaml |
val name = expr | let name = expr |
fun f x y = expr | let f x y = expr |
val rec fib = fn n => if n < 2 then n else fib(n-1) + fib(n-2) or fun fib n = if n < 2 then n else fib(n-1) + fib(n-2) | let rec fib = fun n -> if n < 2 then n else fib (n-1) + fib (n-2) or let rec fib n = if n < 2 then n else fib (n-1) + fib (n-2) |
Type Declarations
| SML | Ocaml |
type t = int -> bool | type t = int -> bool |
type ('a,'b) assoc_list = ('a * 'b) list | type ('a,'b) assoc_list = ('a * 'b) list |
datatype 'a option = NONE | SOME of 'a | type 'a option = None | Some of 'a |
datatype t = A of int | B of u withtype u = t * t | type t = A of int | B of u and u = t * t |
datatype v = datatype t | type v = t = A of int | B of u |
datatype complex = C of real * real fun complex xy = C xy fun coord (C xy) = xy | type complex = C of float * float let complex (x,y) = C (x,y) let coord (C (x,y)) = (x,y) or (note parentheses in type declaration) type complex = C of (float * float) let complex xy = C xy let coord (C xy) = xy |
Matching
| SML | Ocaml |
fun getOpt(NONE, d) = d | getOpt(SOME x, _) = x | let get_opt = function (None, d) -> d | (Some x, _) -> x |
fun getOpt (opt, d) = case opt of NONE => d | SOME x => x | let get_opt (opt, d) = match opt with None -> d | Some x -> x |
fun take 0 xs = [] | take n nil = raise Empty | take n (x::xs) = x :: take (n-1) xs | let rec take n xs = match n, xs with 0, xs -> [] | n, [] -> failwith "take" | n, x::xs -> x :: take (n-1) xs |
Does not have guards – use if | let rec fac = function 0 -> 1 | n when n>0 -> n * fac (n-1) | _ -> raise Hell |
fun foo(p as (x,y)) = (x,p,y) | let foo ((x,y) as p) = (x,p,y) |
Tuples
| SML | Ocaml |
type foo = int * float * string | type foo = int * float * string |
val bar = (0, 3.14, "hi") | let bar = 0, 3.14, "hi" or let bar = (0, 3.14, "hi") |
#2 bar | Does not have tuple selection – use pattern matching instead, e.g. let _,x,_ = bar in x |
#2 | Does not have first-class selectors – use function instead, e.g. function _,x,_ -> x or fun (_,x,_) -> x |
(inputLine stdIn, inputLine stdIn) | Evaluation order is undefined for tuples – use let, e.g. let line1 = read_line () in let line2 = read_line () in (line1, line2) |
Records
| SML | Ocaml |
type foo = {x:int, y:float, s:string ref} Note: record types need not be declared | type foo = {x:int; y:float; mutable s:string} Note: mutable field does not have the same type as a reference |
val bar = {x=0, y=3.14, s=ref ""} | let bar = {x=0; y=3.14; s=""} |
#x bar #y bar !(#s bar) | bar.x bar.y bar.s |
#x | Does not have first-class selectors – use function instead, e.g. fun r -> r.x |
val {x=x, y=y, s=s} = bar val {y=y, ...} = bar or val {x, y, s} = bar val {y, ...} = bar | let {x=x; y=y; s=s} = bar let {y=y} = bar or (since Ocaml 3.12) let {x; y; s} = bar let {y; _} = bar |
{x = 1, y = #y bar, s = #s bar} | {x = 1; y = bar.y; s = bar.s} or {bar with x = 1} |
#s bar := "something" | bar.s <- "something" |
| Does not have polymorphic fields | type bar = {f:'a.'a -> int} |
{a = inputLine stdIn, b = inputLine stdIn} | Evaluation order is undefined for records – use let, e.g. let line1 = read_line () in let line2 = read_line () in {a = line1; b = line2} |
References
| SML | Ocaml |
val r = ref 0 | let r = ref 0 |
!r | !r or r.contents |
r := 1 | r := 1 or r.contents <- 1 |
fun f(ref x) = x | let f {contents=x} = x |
r1 = r2 r1 <> r2 | r1 == r2 r1 != r2 |
Comparisons
| SML | Ocaml |
2 = 2 2 <> 3 | 2 = 2 2 <> 3 |
val r = ref 2 r = r r <> ref 2 | let r = ref 2 r == r r != ref 2 |
(2, r) = (2, r) (2, r) <> (2, ref 2) | Does not have a proper generic equality (on one hand (2, r) != (2, r), on the other (2, r) = (2, ref 2)) |
case String.compare(x,y) of LESS => a | EQUAL => b | GREATER => c | match compare x y with n when n < 0 -> a | 0 -> b | _ -> c |
fun f x y = (x = y) val f : ''a -> ''a -> bool | let f x y = (x = y) val f : 'a -> 'a -> bool Does not have equality type variables – comparison allowed on all types but may raise Invalid_argument exception |
eqtype t | type t Does not have equality types – comparison allowed on all types but may raise Invalid_argument exception |
Lists
| SML | Ocaml |
[1, 2, 3] | [1; 2; 3] |
[(1, 2), (3, 4)] | [1, 2; 3, 4] |
List.length xs | List.length xs |
List.map f xs | List.map f xs |
List.app f xs | List.iter f xs |
List.foldl op+ 0 xs List.foldr op- 100 xs | List.fold_left (+) 0 xs List.fold_right (-) xs 100 |
List.all (fn x => x=0) xs List.exists (fn x => x>0) xs | List.for_all (fun x -> x=0) xs List.exists (fun x -> x>0) xs |
val xys = ListPair.zip (xs, ys) | let xys = List.combine xs ys |
val (xs, ys) = ListPair.unzip xys | let (xs, ys) = List.split xys |
ListPair.app f (xs, ys) | List.iter2 f xs ys |
[inputLine stdIn, inputLine stdIn] | Evaluation order is undefined for lists – use let, e.g. let line1 = read_line () in let line2 = read_line () in [line1; line2] |
Strings
| SML | Ocaml |
"Hello " ^ "world\n" | "Hello " ^ "world\n" |
Int.toString 13 Real.toString 3.141 | string_of_int 13 string_of_float 3.141 |
String.size s | String.length s |
String.substring(s, 1, 2) | String.sub s 1 2 |
String.sub(s, 0) | String.get s 0 or s.[0] |
| Strings are immutable, use CharArray for mutability | String.set s 0 'F' or s.[0] <- 'F' |
Array Functions
| SML | Ocaml |
Array.array(20, 1.0) | Array.make 20 1.0 |
Array.fromList xs | Array.from_list xs |
Array.tabulate(30, fn x => x*x) | Array.init 30 (fun x -> x*x) |
Array.sub(a, 2) | Array.get a 2 or a.(2) |
Array.update(a, 2, x) | Array.set a 2 x or a.(2) <- x |
Array.copy{src=a, si=10, dst=b, di=0, len=20} | Array.blit ~src:a ~src_pos:10 ~dst:b ~dst_pos:0 ~len:20 |
| SML | Ocaml |
fun copyFile(name1, name2) = let val file1 = TextIO.openIn name1 val s = TextIO.inputAll file1 val _ = TextIO.closeIn file1 val file2 = TextIO.openOut name2 in TextIO.output(file2, s); TextIO.closeOut file2 end | let copy_file name1 name2 = let file1 = open_in name1 in let size = in_channel_length file1 in let buf = String.create size in really_input file1 buf 0 size; close_in file1; let file2 = open_out name2 in output_string file2 buf; close_out file2 Caveat: above code actually contains a race condition. |
Exceptions
| SML | Ocaml |
exception Hell | exception Hell |
exception TotalFailure of string | exception Total_failure of string |
raise TotalFailure "Unknown code" | raise (Total_failure "Unknown code") |
expr handle TotalFailure s => ouch() | try expr with Total_failure s -> ouch () |
Local Declarations
| SML | Ocaml |
fun pyt(x,y) = let val xx = x * x val yy = y * y in Math.sqrt(xx + yy) end | let pyt x y = let xx = x *. x in let yy = y *. y in sqrt (xx +. yy) |
local fun sqr x = x * x in fun pyt(x,y) = Math.sqrt(sqr x + sqr y) end | Does not have local – use global declarations, an auxiliary module, or let |
let structure X = F(A) in X.value + 10 end Standard does not have structure declarations in let but some implementations support them | let module X = F (A) in X.value + 10 Experimental language extension |
let open M in expr end | let open M in expr Note: since Ocaml 3.12 |
let datatype t = A | B exception E in expr end | Does not have local type or exception declarations – use global declarations or let module |
Structures
| SML | Ocaml |
structure X :> S = struct type t = int val x = 0 end | module X : S = struct type t = int let x = 0 end |
X :> S | (X : S) |
X : S | Does not have transparent signature ascription – use opaque ascription and with constraints |
open X | include X |
local open X in (* ... *) end | open X (* ... *) |
Functors
| SML | Ocaml |
functor F(X : S) = struct (* ... *) end | module F (X : S) = struct (* ... *) end or module F = functor (X : S) -> struct (* ... *) end |
functor F(X : sig type t end) = body structure X = F (struct type t = int end) or functor F(type t) = body structure X = F(type t = int) | module F (X : sig type t end) = body module X = F(struct type t = int end) |
functor F (X : S) (Y : T) = body Standard does not have higher-order functors but several implementations support them | module F (X : S) (Y : T) = body or module F = functor (X : S) -> functor (Y : T) -> body |
functor F(X : S) = let structure Y = G(X) in Y.A end | Does not have let for modules |
Signatures
| SML | Ocaml |
signature S = sig type t eqtype u val x : t structure M : T end | module type S = sig type t type u val x : t module M : T end |
functor F(X : S) : S Standard does not have higher-order functors but several implementations support them | module F (X : S) : S or module F : functor (X : S) -> S |
include S | include S |
Does not have open in signatures | open X |
structure X : A structure Y : B sharing type X.t = Y.u | Does not have sharing constraints – use with |
S where type t = int | S with type t = int |
S where X = A.B Standard does not have where for structures but several implementations support it – use where type otherwise | S with X = A.B |
signature S = sig signature A signature B = A end Standard does not have nested signatures but some implementations support them | module type S = sig module type A module type B = A end |