ML Dialects and Haskell: SML, OCaml, F#, Haskell

a side-by-side reference sheet

grammar and invocation | variables and expressions | arithmetic and logic | strings | dates and time | arrays | lists | tuples | dictionaries | functions | execution control | exceptions | concurrency | file handles | files | directories | processes and environment | libraries and namespaces | user-defined types | objects | inheritance and polymorphism | net and web | unit tests | debugging and profiling | repl

sml ocaml f# haskell
version used
 
SML NJ 110 4.0 F# 3.0
Mono 3.2
7.4
show version
 
displayed at startup $ ocaml -version $ fsharpi --help $ ghc --version
grammar and invocation
sml ocaml f# haskell
interpreter
 
$ echo 'print_endline "hello"' > hello.ml

$ ocaml hello.ml
$ cat <<EOF > hello.fs
module hello
let main = printfn "hello"
EOF

$ fsharpi --quiet --exec hello.fs
$ echo 'main = putStrLn "hello"' > hello.hs

$ runghc hello.hs
shebang $ cat <<EOF > hello.ml
#!/usr/bin/env ocaml

print_endline "hello";;
EOF

$ chmod +x hello.ml
$ ./hello.ml
$ cat <<EOF > hello.fs
#light (*
  exec fsharpi --exec $0 --quiet
*)

module hello

printfn "hello"
EOF

$ chmod +x hello.fs
$ ./hello.fs
$ cat <<EOF > hello.hs
#!/usr/bin/env runghc

main = putStrLn "hello"
EOF

$ chmod +x hello.hs
$ ./hello.hs
bytecode compiler and interpreter $ echo 'print_endline "hello";;' > hello.ml
$ ocamlc -o hello hello.ml
$ ocamlrun hello
$ echo 'printfn "hello"' > hello.fs
$ fsharpc hello.fs
$ mono hello.exe
none
native compiler $ echo 'print_endline "hello";;' > hello.ml
$ ocamlopt hello.ml -o hello
$ ./hello
none $ echo 'main = putStrLn "hello"' > hello.hs
$ ghc -o hello hello.hs
$ ./hello
library which is always imported Pervasives Core Prelude
statement terminator ; ;; ;; next line has equal or less indentation, or ;
blocks ( expr ; ) ( expr ; )
begin expr ; end
( expr ; )
begin expr ; end
offside rule or { }
end-of-line comment none none // comment -- comment
multiple line comment (* comment
another comment *)
(* comment
another comment
*)
(* comment
another comment
*)
{- comment
another comment
-}
variables and expressions
sml ocaml f# haskell
write-once variable
 
val a = 3; let n = 1 + 2;; let n = 1 + 2 n = 3
modifiable variable val a = ref 3;
a := 4;
!a + 7;
let n = ref 3;;
n := 4;;
!n + 7;;
let n = ref 3
n := 4
!n + 7
n <- return 3
unit type and value unit
()
unit
()
unit
()
()
()
conditional expression val x = 3;
if x < 0 then ~x else x;
let n = -3;;
let absn = if n < 0 then -n else n;;
let n = -3
let absn = if n < 0 then -n else n
n = -3
let absn = if n < 0 then -n else n
branch type mismatch (* compilation error: *)
if true then "hello" else 3;
(* compilation error: *)
if true then "hello" else 3;;
(* compilation error: *)
if true then "hello" else 3
-- compilation error:
if True then "hello" else 3
null
 
NONE None None

Also this value returned by .NET library functions. It has a type distinct from None:
null
Nothing
nullable type type list_option_int = int option list;

val list = [SOME 3,NONE, SOME ~4];
type list_option_int = int option list;;

let list = [Some 3; None; Some (-4)];;
list = [Just(3), Nothing, Just(-4)]
null test match foo with
  | None -> true
  | _ -> false;;
coalesce val foo = SOME 3;

(* raises exception if NONE: *)
valOf foo;

(* evaluates to 0 if NONE: *)
getOpt (foo, 0);
match foo with
  | None -> 0
  | Some n -> n;;
import Data.Maybe

let foo = Just(3)
raises exception if Nothing:
fromJust foo

let intId x = x
evaluates to 0 if Nothing:
maybe 0 intId foo
nullif match foo with
  | -999 -> None
  | n -> Some n;;
expression type declaration float 1 float 1 1 :: Double
let ... in ... val z =
let
  val x = 3.0
  val y = 2.0 * x
in
  x * y
end;
let z =
  let x = 3.0 in
  let y = 2.0 *. x in
  x *. y;;
let z =
  let x = 3.0 in
  let y = 2.0 * x in
  x * y
z = let x = 3.0
        y = 2.0 * x
    in x * y
where none none none z = x * y
  where x = 3.0
        y = 2.0 * x
arithmetic and logic
sml ocaml f# haskell
boolean type
 
bool bool bool Bool
true and false
 
true false true false true false True False
logical operators andalso orelse not && || not && || not && || not
relational operators = <> < > <= >= = <> < > <= >= = <> < > <= >= == /= < > <= >=
min and max min 1 2
max 1 2
min 1 2
max 1 2
min 1 2
max 1 2
integer type int int

other integer types:
int32 int64 nativeint
int

other integer types:
int32 int64 nativeint
Integer
integer literal negative integer:
~4
int, int64, and nativeint literals:
12 12L 12n

literals can contain underscores:
1_000_000

this parses as an expression:
-4
-4 an expression, not a literal:
-4
float type real float float Double
integer operators + - * div mod + - * / mod
mod //is an infix operator
+ - * / % + - * div rem
div and rem are functions, not infix operators
float operators
 
+ - * / +. -. *. /. + - * / + - * /
add integer and float real 3 + 7.0; float 3 +. 7.0 float 3 + 7.0 3 + 7.0
integer division
and remainder
7 div 3
7 mod 3
real 7 / real 3
7 / 3
7 mod 3
7 / 3
7 % 3
div 7 3
rem 7 3
integer division by zero raises Division_by_zero System.DivideByZeroException Exception: divide by zero
float division
 
float 7 /. float 3 float 7 / float 3 7 / 3
float division by zero infinity nan or neg_infinity infinity nan or neg_infinity evaluates to Infinity, NaN, or -Infinity, values which do not have literals
power Math.pow (2.0, 32.0); 2.0 ** 32.0 2.0 ** 32.0 2 ** 32

-- syntax error if exponent not an integer:
2 ^ 32
sqrt
 
Math.sqrt 2.0 sqrt 2.0 sqrt 2.0 sqrt 2
sqrt -1 Math.sqrt ~1.0 evaluates to nan sqrt (-1.0):
nan
nan sqrt (-1) evaluates to NaN, a value which has no literal
transcendental functions Math.exp Math.ln
Math.sin Math.cos Math.tan
Math.asin Math.acos Math.atan
Math.atan2
exp log
sin cos tan
asin acos atan
atan2
exp log
sin cos tan
asin acos atan
atan2
exp log
sin cos tan
asin acos atan
atan2
transcendental constants Math.pi
Math.e
4.0 *. atan 1.0
exp 1.0
System.Math.PI
System.Math.E
pi
exp 1
float truncation round 3.14
trunc 3.14
floor 3.14
ceil 3.14
truncate 3.14
none
floor 3.14 returns float
ceil 3.14 returns float
truncate 3.14
round 3.14
floor 3.14 returns float
ceil 3.14 returns float
truncate 3.14
round 3.14
floor 3.14
ceiling 3.14
absolute value
and signum
abs (-7)
abs_float (-7.0)
no signum
abs -7
abs -7.0
sign -7
sign -7.0
abs (-7)
signum (-7)
integer overflow Overflow exception modular arithmetic modular arithmetic has arbitrary length integers
float overflow infinity infinity evaluates to Infinity, a value which has no literal
arbitrary length integer open Big_int;;

let n = big_int_of_int 7;;
let m = big_int_of_int 12;;
// System.Numerics.BigInteger:
let n = 7I
let m = 12I
-- Integer is arbitrary length type:
let n = 7
let m = 12
arbitrary length integer operators add_big_int n m
sub_big_int n m
mult_big_int n m
div_big_int n m (* quotient *)
mod_big_int n m

eq_big_int n m
lt_big_int n m
gt_big_int n m
le_big_int n m
ge_big_int n m
n + m
n - m
n * m
n / m
n % m

n = m
n < m
n < m
n <= m
n >= m
n + m
n - m
n * m
div n m
mod n m

n == m
n < m
n < m
n <= m
n >= m
rational type Ratio Integer
rational construction import Data.Ratio

1 % 7
rational decomposition import Data.Ratio

numerator (1 % 7)
denominator (1 % 7)
complex type Complex.t Complex Double
complex constants Complex.zero
Complex.one
Complex.i
complex operators Complex.add z w;;
Complex.sub z w;;
Complex.mul z w;;
Complex.div z w;;
complex construction {Complex.re=1.0; Complex.im=2.0} System.Numerics.Complex(1.0, 2.0) import Data.Complex

1 :+ 2.0
complex decomposition let z = {Complex.re=1.0; Complex.im=2.0};;

z.Complex.re;;
z.Complex.im;;
Complex.arg z;;
Complex.norm z;;
Complex.conj z;;
import Data.Complex

realPart (1 :+ 2)
imagPart (1 :+ 2)
phase (1 :+ 2)
magnitude (1 :+ 2)
conjugate (1 :+ 2)
random number
uniform int, uniform float, normal float
Random.int 100
Random.float 1.0
none
let rnd = System.Random()

rnd.Next(0, 100)
rnd.NextDouble()
none
-- $ cabal install random
import System.Random

getStdRandom (randomR (0, 99))
getStdRandom (randomR (0.0, 1.0))
none
random seed
set, get, restore
Random.init 17;;
let seed = Random.get_state();;
Random.set_state seed;;
let rnd = System.Random(17)
none
none
-- $ cabal install random
import System.Random

setStdGen $ mkStdGen 17
seed <- getStdGen
setStdGen seed
bit operators 1 lsl 4
1 lsr 4
1 land 3
1 lor 3
1 lxor 3
lnot 1
1 <<< 4
1 >>> 4
1 &&& 3
1 ||| 3
1 ^^^ 3
~~~ 1
import Data.Bits

x = 1 :: Integer
y = 3 :: Integer

shiftL x 4
shiftR x 4
x .&. y
x .|. y
xor x y
complement x
binary, octal, and hex literals 0b101010
0o52
0x2a
0b101010
0o52
0x2a
none
052
0x2a
radix
strings
sml ocaml f# haskell
string type
 
string string string String
string literal
 
"Hello, World!" "Hello, World!" "Hello, World!" "Hello, World!"
newline in literal no yes no
literal escapes \000 \a \b \f \n \r \t \v \040 \b \n \r \t \" \' \\
\ooo \xhh
\b \n \r\ t \" \' \\
\uhhhh \Uhhhhhhhh
\a \b \f \n \r \t \v \" \& \' \\
\oo... \d... \xh...

Octal, decimal, and hex escapes denote Unicode characters and can contain anywhere from 1 to 7 digits. The max values are \o4177777, \1114111, and \x10ffff. The \& escape does not represent a character, but can separate a numeric backslash escape sequence from a following digit.
format string sprintf "foo %s %d %.2f" "bar" 7 3.1415 import Text.Printf

printf "foo %s %d %.2f" "bar" 7 3.1415
concatenate
 
"Hello" ^ ", " ^ "World!" "Hello" ^ ", " ^ "World!" "Hello" + ", " + "World!" "Hello" ++ ", " ++ "World!"
replicate
 
String.make 80 '-' String.replicate 80 "-" concat ( replicate 80 "-" )
translate case
to upper, to lower
String.uppercase "hello"
String.lowercase "HELLO"
"hello".ToUpper()
"HELLO".ToLower()
import Data.Char

map toUpper "hello"
map toLower "HELLO"
capitalize
 
String.capitalize "hello"
trim
both sides, left, right
String.trim " hello " " hello ".Trim()
" hello".TrimStart()
"hello ".TrimEnd()
pad
on left, on right
"hello".PadLeft(10, ' ')
"hello".PadRight(10, ' ')
number to string "two: " ^ string_of_int 2
"pi: " ^ float_of_string 3.14
"two: " + string 2
"pi: " + string 3.14
"two: " ++ (show 2)
"pi: " ++ (show 3.14)
string to number Int.toString 3
Real.toString 3.14
7 + int_of_string "12"
73.9 +. float_of_string ".037"
7 + int "12"
73.9 + float ".037
7 + (read "12")::Integer
73.9 + (read "0.037")::Double
raises exception if string doesn't completely parse
join
 
System.String.Join(" ", ["do"; "re"; "mi"])
split
 
"do re mi".Split(' ')
character type
 
char char char Char
character literal #"h" 'h' 'h' 'h'
length
 
size "hello" String.length "hello" "hello".Length length "hello"
index of substring "hello".IndexOf("hell")
extract substring substring ("hello",0,4) String.sub "hello" 0 4 "hello".Substring(0, 4) drop 0 (take 4 "hello")
extract character String.sub ("hello", 0) "hello".[0] "hello".[0] "hello" !! 0
chr and ord ord #"a"
chr 97
Char.code 'a'
Char.chr 97
int 'a'
char 97
Char.ord 'a'
Char.chr 97
dates and time
sml ocaml f# haskell
date and time types ClockTime CalendarTime TimeDiff
current date and time import Time

t <- getClockTime
current unix epoch open Unix;;

(* float: *)
time();;
import System.Time

getClockTime >>= (\(TOD sec _) -> return sec)
arrays
sml ocaml f# haskell
literal
size
lookup
update
out-of-bounds
lists
sml ocaml f# haskell
literal [1, 2, 3] [1; 2; 3] [1; 2; 3] [1, 2, 3]
empty list
 
[] []
empty list test let list = [1; 2; 3];;

list == []
let list = [1, 2, 3]

list == []
null list
cons
 
1 :: [2, 3] 1 :: [2; 3] 1 :: [2; 3] 1 : [2, 3]
head
 
List.hd [1, 2, 3] List.hd [1; 2; 3] List.head [1; 2; 3] head [1, 2, 3]
tail
 
List.tl [1, 2, 3] List.tl [1; 2; 3] List.tail [1; 2; 3] tail [1, 2, 3]
head and tail of empty list exceptions exceptions
length
 
List.length [1, 2, 3] List.length [1; 2; 3] List.length [1; 2; 3] length [1, 2, 3]
nth element
 
List.nth ([1, 2, 3], 0) List.nth [1; 2; 3] 0 List.nth [1; 2; 3] 0 [1, 2, 3] !! 0
element index import Data.list

-- Just 1:
elemIndex 8 [7, 8, 9]

-- Nothing:
elemIndex 10 [7, 8, 9]
update
concatenate
two lists, list of lists
[1, 2] @ [3, 4]
List.concat [[1, 2], [3, 4]]
[1; 2] @ [3; 4]
List.append [1; 2] [3; 4]

List.concat [[1; 2]; [3; 4]]
[1; 2] @ [3; 4]
List.append [1; 2] [3; 4]

List.concat [[1; 2]; [3; 4]]
[1, 2] ++ [3, 4]

concat [[1, 2], [3, 4]]
last
and butlast
last [1, 2, 3]
init [1, 2, 3]
take
 
take 2 [1, 2, 3]
drop
 
drop 2 [1, 2, 3]
iterate fun f i = print ((Int.toString i) ^ "\n");
List.app f [1, 2, 3];
let f i =
  print_endline (string_of_int i);;

List.iter f [1; 2; 3];;
let f i =
  System.Console.WriteLine(string i)

List.iter f [1; 2; 3]
mapM_ print [1, 2, 3]
reverse
 
List.rev [1, 2, 3] List.rev [1; 2; 3] List.rev [1; 2; 3] reverse [1, 2, 3]
sort List.sort min [1; 3; 2; 4]
List.sort max [1; 3; 2; 4]
List.sort [1; 3; 2; 4] import Data.List

sort [1, 3, 2, 4]
map List.map (fn (x) => x + 2) [1, 2, 3]; List.map (( * ) 2) [1; 2; 3] List.map (( * ) 2) [1; 2; 3] map (\x -> x * x) [1, 2, 3]
filter
 
List.filter (fn (x) => x > 2) [1, 2, 3]; List.filter ((<) 2) [1; 2; 3] List.filter ((<) 2) [1; 2; 3] filter (\x -> x > 2) [1, 2, 3]
fold from left List.foldl (op +) 0 [1, 2, 3]; List.fold_left (+) 0 [1; 2; 3] List.fold (-) 0 [1; 2; 3] foldl (+) 0 [1, 2, 3]
fold from right
 
List.fold_right (-) [1; 2; 3] 0 List.foldr (op -) 0 [1, 2, 3]; foldr (-) 0 [1, 2, 3]
membership
 
List.mem 3 [1; 2; 3] elem 3 [1, 2, 3]
universal test
 
List.for_all (fun x -> x > 2) [1; 2; 3];; List.forall (fun x -> x > 2) [1; 2; 3] all (\x -> x > 2) [1, 2, 3]
existential test
 
List.exists (fun x -> x > 2) [1; 2; 3];; List.exists (fun x -> x > 2) [1; 2; 3] any (\x -> x > 2) [1, 2, 3]
zip lists (* list of tuples *)
List.combine [1; 2; 3] ['a'; 'b'; 'c']
-- list of tuples:
zip [1, 2, 3] ['a', 'b', 'c']
tuples
sml ocaml f# haskell
literal (1, "hello", true) (1, "hello", true) (1, "hello", true) (1, "hello", True)
lookup #1 (1, "hello", true) match (1, "hello", true) with _, x, _ -> x match (1, "hello", true) with _, x, _ -> x (\(a, _, _) -> a) (1, "hello", True)
pair lookup #1 (12,"December")
#2 (12,"December")
fst (12, "December")
snd (12, "December")
fst (12, "December")
snd (12, "December")
fst (12, "December")
snd (12, "December")
dictionaries
sml ocaml f# haskell
functions
sml ocaml f# haskell
define function fun average a b = ( a + b ) / 2.0; let average a b = ( a +. b ) /. 2.0;; let average a b = ( a + b ) / 2.0 average a b = (a + b) / 2.0
invoke function (* 4.5: *)
average 1.0 2.0 +. 3.0;;

(* 3.0: *)
average 1.0 (2.0 +. 3.0);;
// 4.5:
average 1.0 2.0 + 3.0

// 3.0:
average 1.0 (2.0 + 3.0)
-- 4.5:
average 1 2 + 3

-- 3.0:
average 1 (2 + 3)
average 1 $ 2 + 3
named parameter let subtract ~m ~s = m - s;;

subtract ~s: 3 ~m: 7;;
none
named parameter default value let logarithm ?(base = (exp 1.0)) x = log x /. (log base);;

logarithm 2.718;;
logarithm ~base: 2.0 10.0;;
none
piecewise defined function val to_s = fn Red => "red"
  | Green => "green"
  | Blue => "blue";
let to_s = function Red -> "red"
  | Green -> "green"
  | Blue -> "blue";;
to_s Red = "red"
to_s Green = "green"
to_s Blue = "blue"
recursive function fun range a b =
  if a > b then []
  else a :: range (a + 1) b;
let rec range a b =
if a > b then []
else a :: range (a+1) b;;
range a b = if a > b then [] else a : range (a+1) b
mutually-recursive-functions let rec even n = if n = 0 then true else odd (n-1)
and odd n = if n = 0 then false else even (n-1);;
anonymous function fn x => fn y => (x + y) / 2.0 fun x -> fun y -> (x +. y) /. 2.0 fun x -> fun y -> (x + y) / 2.0 \x y -> (x+y) / 2.0
infix operator in prefix position (op * ) (3, 4) ( * ) 3 4;; ( * ) 3 4
function in infix position none add x y = x + y
3 ‘add` 4
currying un plus x y = x + y;
val plus2 = plus 2;
plus2 7;
let plus2 = (+) 2;; plus2 = (+) 2
composition f x = x + 2
g x = x * 3
(f . g ) 4
function composition operator fun double x = 2 * x;
val quadruple = double o double;
none double x = 2 * x
quadruple x = double . double
lazy evaluation let arg1 x y = x;;

arg1 7 (lazy (1/0) );;
lazy evaluation is default:
arg1 x y = x

arg1 7 (error "bam!")
strict evaluation default behavior default behavior arg1 x y = seq y x

arg1 7 (error "bam!")
execution control
sml ocaml f# haskell
if f x > 0 then
  print "pos\n"
else
  ();
if x > 0 then
  print_endline "pos";;
if x > 0 then
  printfn "pos"
if x > 0
  then putStrLn "pos"
  else return ()
if else-if else if x > 0 then print "pos" else if x < 0 then print "neg" else print "zero"; if x > 0 then
  print_endline "pos"
else
  if x < 0 then
    print_endline "neg"
  else
    print_endline "zero";;
if x > 0 then
  printfn "pos"
else
  if x < 0 then
    printfn "neg"
  else
    printfn "zero"
if x > 0
  then putStrLn "pos"
  else if x < 0
    then putStrLn "neg"
    else putStrLn "zero"
sequencing print_endline "one";
print_endline "two";
print_endline "three";;
printfn "one"
printfn "two"
printfn "three"
do
  putStrLn "one"
  putStrLn "two"
  putStrLn "three"
while let i = ref 0;;

while !i < 10 do
  print_endline (string_of_int !i);
  i := !i + 1
done;;
let i = ref 0

while !i < 10 do
  printfn "%d" !i
  i := !i + 1
for for i = 1 to 10 do
  let s = string_of_int i in
  print_endline s
done;;
for in reverse for i = 10 downto 1 do
  let s = string_of_int i in
  print_endline s
done;;
list iteration none
loop let rec loop i =
  if i <= 10 then begin
    print_endline (string_of_int i);
    loop (i+1)
  end in
loop 0;;
exceptions
sml ocaml f# haskell
raise error raise (Failure "bam!");;
or
failwith "bam!";;
error "bam!"
handle error let x = try 1 / 0 with Division_by_zero -> 0;;
type of exceptions exn
user defined exception exception Foo of string;;
raise (Foo "invalid input");;
standard exceptions Division_by_zero
Failure string
Not_found
Invalid_argument string
Match_failure (string, int, int)
Assert_failure (string, int, int)
Out_of_memory
Stack_overflow
assert assert(1 = 0);;
concurrency
sml ocaml f# haskell
file handles
sml ocaml f# haskell
standard file handles stdin stdout stderr stdin stdout stderr import System.Posix.IO

stdInput stdOutput stdError
read line from stdin let line = read_line();; line <- getLine
end-of-file behavior raises End_of_file when last data is returned, hIsEOF will return True. Reading after end-of-file throws an exception.
chomp
write line to stdout print_endline "lorem ipsum";; printfn "lorem ipsum" putStrLn "lorem ipsum"
write formatted string to stdout
open file for reading let f = open_in "/etc/passwd";; import System.IO

f <- openFile "/etc/hosts" ReadMode
open file for writing let f = open_out "/tmp/ocaml.out";; import System.IO

f <- openFile "/tmp/test" WriteMode
open file for appending import System.IO

f <- openFile "/tmp/err.log" AppendMode
close file import System.IO

hClose f
i/o errors
read line fun displayFile(file: string) =
  let
    val f = TextIO.openIn file
    fun iter(s: string option) =
    case s of
    NONE =>
      (TextIO.closeIn f)
    | SOME(line) =>
      (print line;
      iter(TextIO.inputLine f))
  in
    iter(TextIO.inputLine f)
  end
displayFile("/etc/passwd");
let ic = open_in "/etc/passwd" in
let line = input_line ic in
print_endline line;;
import IO

readAndPrintLines h = do
  eof <- hIsEOF h
  if eof
    then return ()
    else do
      line <- hGetLine h
      putStrLn line
      readAndPrintLines h

main = do
  h <- openFile "/etc/passwd" ReadMode
  readAndPrintLines h
iterate over file by line
read file into array of strings
read file into string
write string
write line val file = "/tmp/test-sml";
val f = TextIO.openOut file;
TextIO.output(f, "hello out\n");
TextIO.closeOut f;
open Printf
let oc = open_out "/tmp/test-ocaml" in
fprintf oc "hello out\n";
close_out oc;;
s = "hello out\n"
f = "/tmp/test-haskell"
main = writeFile f s
flush file handle
end-of-file test
get and set filehandle position
files
sml ocaml f# haskell
file test, regular file test open Unix

try Some (stat "/etc/hosts") with
  Unix_error (ENOENT, _, _) -> None

(stat "/etc/hosts").st_kind = S_REG
import System

Directory.doesFileExist "/etc/hosts"

import Control.Monad
import System.Posix.Files

liftM isRegularFile (getFileStatus "/etc/hosts")
file size (stat "/etc/hosts").st_size import Control.Monad
import System.Posix.Files

liftM fileSize (getFileStatus "/etc/hosts")
is file readable, writable, executable open Unix

try access "/tmp/bar" [R_OK]; true with
  Unix.Unix_error (EACCES, _, _) -> false;;
try access "/tmp/bar" [W_OK]; true with
  Unix.Unix_error (EACCES, _, _) -> false;;
try access "/tmp/bar" [X_OK]; true with
  Unix.Unix_error (EACCES, _, _) -> false;;
import Control.Monad

liftM readable
  (getPermissions "/etc/hosts")
liftM writable
  (getPermissions "/etc/hosts")
liftM executable
  (getPermissions "/etc/hosts")
set file permissions open Unix

chmod "/tmp/foo" 0o755
import System.Posix.Files

setFileMode "/tmp/foo" ownerModes
setFileMode "/tmp/foo" groupReadMode
setFileMode "/tmp/foo" groupExecuteMode
setFileMode "/tmp/foo" otherReadMode
setFileMode "/tmp/foo" otherExecuteMode
copy file, remove file, rename file open Unix

??
unlink "/tmp/foo"
rename "/tmp/bar" "/tmp/foo"
import System.Directory

copyFile "/tmp/foo" "/tmp/bar"
removeFile "/tmp/foo"
renameFile "/tmp/bar" "/tmp/foo"
create symlink, symlink test, readlink open Unix

symlink "/etc/hosts" "/tmp/hosts"
(lstat "/tmp/hosts").st_kind = S_LNK
readlink "/tmp/hosts"
import System.Posix.Files

createSymbolicLink "/etc/hosts" "/tmp/hosts"
??
readSymbolicLink "/tmp/hosts"
generate unused file name open Filename

(* prefix and suffix: *)
temp_file "foo" ".txt"
directories
sml ocaml f# haskell
build pathname open Filename

concat "/etc" "hosts"
import System.FilePath ((</>))

let path = "/etc" </> "hosts"
dirname and basename open Filename

dirname "/etc/hosts"
basename "/etc/hosts"
import System.FilePath

takeFileName "/etc/hosts"
takeDirectory "/etc/hosts"
iterate over directory by file import System

-- returns IO [FilePath]
Directory.getDirectoryContents "/etc"
make directory (* opam install fileutils *)
open FileUtil

mkdir ~parent:true "/tmp/foo/bar"
import System.Directory

createDirectoryIfMissing True
  "/tmp/foo/bar"
remove empty directory open Unix

rmdir "/tmp/foodir"
import System.Directory

removeDirectory "/tmp/foodir"
remove directory and contents import System.Directory

removeDirectoryRecursive "/tmp/foodir"
directory test import System

Directory.doesDirectoryExist "/tmp"
temporary directory
processes and environment
sml ocaml f# haskell
command line arguments for i = 0 to Array.length Sys.argv - 1 do
  print_endline i Sys.argv.(i)
done
import System

printArgs args = do
  if length args == 0
    then return ()
    else do
      putStrLn (head args)
      printArgs (tail args)
main = do
  a <- getArgs
  printArgs a
program name
 
import System

s <- getProgName
getopt
get and set environment variable
 
open Unix

s = getenv "HOME"
putenv "PATH" "/bin"
import System.Posix.Env

s <- getEnv "HOME"
putEnv "PATH=/bin"
get pid, parent pid open Unix

let pid = getpid()
let ppid = getppid()
import System.Posix.Process

pid <- getProcessID
ppid <- getParentProcessID
get user id and name let uid = getuid()
let username =
  (getpwuid (getuid())).pw_name
import System.Posix.User

uid <- getRealUserID
username <- getLoginName
exit
 
exit 0

exit 1
import System.Exit

exitWith ExitSuccess

to return nonzero status:
exitWith (ExitFailure 1)
set signal handler
 
external command
 
import System.Cmd

rawSystem "ls" ["-l", "/tmp"]
escaped external command
 
backticks
 
libraries and namespaces
sml ocaml f# haskell
namespace example Foo/Bar.hs
module Foo.Bar where
  data Baz = Baz
  say Baz = putStrLn "hello"

Main.hs
module Main where
import Foo.Bar
baz = Baz
main = say baz

to compile and run
$ ghc -c Foo/Bar.hs
$ ghc Main.hs
$ ./Main
hello
namespaces values, constructors, type variables, type constructors, type classes, modules
file name restrictions module Foo.Bar must be in Foo.ml module Foo.Bar must be in Foo/Bar.hs
namespace open Graphics;; import Data.Bytestring
namespace creation put code in file MODULE_NAME.ml
namespace alias module Gr = Graphics;; import qualified Data.Bytestring as B
namespace separator . .
subnamespace in A.ml:
module B =
sig
  val display_instruction : unit -> unit
end =
struct
  let msg = "attack"
  let display_instruction () = print_endline msg
end
in client source:
A.B.display_instruction;;
package manager setup do this once:
$ opam init

for each shell session:
$ eval $(opam config env)
package manager
search; install; list installed
$ opam search utop
$ opam install utop
$ opam list --installed
$ cabal list parsec
$ cabal install parsec
$ cabal list --installed
compile app using package
user-defined types
sml ocaml f# haskell
type synonym
 
type name = string; type name = string;; type name = string type Name = String
sum type datatype color = Red | Green | Blue; type color = Red | Green | Blue;;

let col = Red;;

(* evaluates to true: *)
col < Green;;
type color = Red | Green | Blue

let col = Red

// evaluates to true:
col < Green
data Color = Red | Green | Blue

col = Red

-- this won’t compile:
col < Green
tuple product type with one field datatype special_int = SpecialInt of int;

val x = SpecialInt 7;
type special_int = SpecialInt of int;;

let n = SpecialInt 7;;
type special_int = SpecialInt of int

let n = SpecialInt 7
data SpecialIntType = SpecialInt Integer

n = SpecialInt 7
tuple product type with two fields datatype int_pair = IntPair of int * int;

val y = IntPair (7, 11);
type int_pair = IntPair of int * int;;

let p = IntPair (7, 11);;
type int_pair = IntPair of int * int

let p = IntPair (7, 11)
data IntPairType = IntPair Integer Integer

p = IntPair 7 11
record product type type customer = {id:int, name:string, address:string} type customer = {
  id: int;
  name: string;
  address: string
};;
type customer = {
  id: int;
  name: string;
  address: string
}
data CustomerType = Customer {
  customerId :: Integer,
  name :: String,
  address :: String
}
record product type literal {id=7, name="John", address="Topeka, KS"} let cust = {
  id=7;
  name="John";
  address="Topeka, KS"
};;
{id=7; name="John"; address="Topeka, KS"} Customer {
  customerId=7,
  name="John",
  address="Topeka, KS" }
generic type datatype ('a, 'b) twosome =
  Twosome of 'a * 'b;

val z = Twosome ("pi", 3.14);
type ('a, 'b) twosome =
  Twosome of 'a * 'b;;

let p = Twosome ("pi", 3.14);;
type ('a, 'b) twosome =
  Twosome of 'a * 'b

let p = Twosome ("pi", 3.14)
data TwosomeType a b = Twosome a b

p = Twosome ("pi", 3.14)
recursive type datatype binary_tree =
  Leaf of int
  | Tree of binary_tree * binary_tree;
type binary_tree =
  | Leaf of int
  | Tree of binary_tree * binary_tree;;
type binary_tree =
  | Leaf of int
  | Tree of binary_tree * binary_tree
data BinaryTree = Leaf Integer | Tree BinaryTree BinaryTree
pattern match sum type val c = Red;

case c of Red => "red"
  | Blue => "blue"
  | Green => "green";
let col = Red;;

let s = match col with
  | Red -> "red"
  | Blue -> "blue"
  | Green -> "green";;
c = Red
case c of Red -> "red"
  Green -> "green"
  Blue -> "blue"
pattern match product type
pattern match guard none; use if match i with j when i < 0 -> -j | j -> j;; none, use if or piecewise function definition
pattern match catchall fun to_s c = case c of Red => "red" | _ => "not red"; let to_s c = match c with Red -> "red" | _ -> "not red";;
to_s Green;;
c = Green
case c of Red -> "red"; _ -> "not red"
objects
sml ocaml f# haskell
class definition class counter = object
  val mutable n = 0
  method incr = n <- n+1
  method get = n
end;;
object creation let c = new counter;;
method invocation c#incr;;
c#get;;
field access none
inheritance and polymorphism
sml ocaml f# haskell
overload function
inheritance
net and web
sml ocaml f# haskell
unit test
sml ocaml f# haskell
debugging and profiling
sml ocaml f# haskell
repl
sml ocaml f# haskell
invoke repl $ sml $ ocaml

Use this if you want history:
$ rlwrap ocaml

The utop toplevel, which can be installed via opam, also provides history.
Mono:
$ fsharpi

In visual studio, highlight code and press ALT+ENTER.
$ ghci
repl limitations Must use let to define values and functions; when defining functions with multiple equations the equations must be separated by semicolons; the clauses of case/of statements must be separated by semicolons; it is not possible to define data types.
repl last value it none it it
help none :?
quit ^D #quit;;
inspect type repl displays the type of any expression entered let a = 3
:type a
inspect namespace module Unix = Unix;;
load source file use "hello.ml"; #use "hello";; :edit hello.hs
:load hello
load package consider adding to .ocamlinit:
#use "topfind";;
# thread;;
#require "core";;
open Core.Std;;
search path #directory "libdir";;
set search path on command line ocaml -Ilibdir
_______________________________________________ _______________________________________________ _______________________________________________ _______________________________________________

version used

Versions used to test the code samples in this sheet.

show version

How to get the version.

Grammar and Invocation

interpreter

How to run the interpreter on a file of source code.

shebang

How to use the interpreter in a shebang.

bytecode compiler and interpreter

How to compile source to bytecode and run it.

ocaml:

It is not necessary to invoke ocamlrun on the bytecode; the bytecode can be executed directly because the bytecode compiler puts a shebang invocation at the top of the file.

native compiler

How to compile source to native code and run it.

library which is always imported

The name of the library containing the types and functions which are always available.

statement terminator

ocaml:

;; is the ocaml statement separator. It is not necessary at the end of the line if the following line starts with an open or let keyword or at the end of the file.

blocks

How to define a block of statements.

end-of-line comment

A comment terminated by the end of the line.

multiple line comment

A comment with a start and end delimiter which can span multiple lines.

ocaml:

(* *) style comments can be nested.

Variables and Expressions

write-once variable

How to define a variable which can be set at run-time but cannot be modified after it is set.

modifiable variable

How to define a modifiable variable.

unit type and value

The notation for the unit type and the unit value. In all languages the notation for the unit value is the same as the notation for an empty tuple.

The unit value is a common return value of functions which perform side effects.

conditional expression

The syntax for a conditional expression.

branch type mismatch

What happens if the two branches of a conditional expression don't have the same type.

null

A value used somewhat paradoxically to indicate the absence of a value.

Types which can contain a null value are called option types.

nullable type

null test

coalesce

expression type declaration

How to explicitly declare the type of an expression.

let ... in ...

How to define local variables.

ocaml:

OCaml uses let to define a value and let with in to define values in a local scope. OCaml follows the usage of the original dialect of ML in this respect.

OCaml can define multiple values with a single let and in by conjoining the definitions with and. The definitions are performed in parallel, so later definitions cannot use the earlier definitions:

let z = 
let x = 3
and y = 4 in
x * y;;

haskell:

Haskell uses let with in to define local scope. In addition, ghci uses let without in to define values.

where

How to define local variables with definitions after the expression that uses them.

Arithmetic and Logic

boolean type

The type for boolean values.

true and false

The literals for true and false.

logical operators

The logical operators: and, or, and not.

relational operators

Operators for performing comparisons.

min and max

The binary functions min and max.

integer types

The most commonly used numeric types.

integer literal

Integer literals.

haskell:

Haskell does not have negative integer literal syntax. The negative sign parses as a unary prefix operator. It may be necessary to put parens around a negative integer constant:

-- syntax error:
1 + -3

-- ok:
1 + (-3)

float type

Floating point types.

integer operators

The integer operators.

float operators

The floating point operators. Note that in the OCaml the floating point operators are different from the integer operators.

add integer and float

How to add an integer and a float.

ocaml:

OCaml also can convert a integer to float with float_of_int.

integer division

How to find the quotient of two integers; how to find the remainder of two integers.

integer division by zero

The result of dividing an integer by zero.

float division

float division by zero

The result of division by zero.

float exponentiation

How to exponentiate a float.

float functions

The square root function; the natural exponential and natural logarithm functions; the trigonometric functions.

arithmetic truncation

Ways to convert a float to a nearby integer.

ocaml:

This definition of round handles negative numbers correctly:

let round x = int_of_float (floor (x +. 0.5))

power

How to perform exponentiation.

ocaml:

How to define a function which computes the power of an integer:

let integer_exponent b e =
  let rec aux x i =
    if i = e then x else aux (x * b) (i + 1)
  in
  aux 1 0;;

sqrt -1

The result of taking the square root of a negative number.

transcendental functions

transcendental constants

integer overflow

What happens when expression evaluates to an integer that is larger than what can be stored.

float overflow

The result of float overflow.

Ocaml has literals for infinity and negative infinity, but Scala and Haskell do not.

rational type

rational construction

rational decomposition

complex type

complex construction

complex decomposition

random number

How to generate a uniformly distributed random integer; how to generate a uniformly distributed float; how to generate a normally distributed float.

random seed

How to set a random seed. How to get and restore the state of a random number generator.

bit operators

The bit operators.

ocaml:

Also has operators which perform arithmetic shift: asl and asr. When performing an arithmetic shift, the sign of the integer is preserved.

haskell:

Haskell does not assign a default size or type to numeric literals. Hence numeric literals must have their type declared for bit operations to be performed on them.

binary, octal, and hex literals

radix

Strings

string type

The types for strings and characters.

string literal

The syntax for a string literal.

newline in literal

literal escapes

format string

concatenate

How to concatenate strings.

f#:

F# supports (with a warning) the ^ operator for compatibility with OCaml.

replicate

translate case

How to convert a string to uppercase; how to convert a string to lowercase; how to capitalize the first character.

capitalize

trim

pad

number to string

string to number

How to parse numeric types from string; how to convert numeric types to strings.

ocaml:

To convert a string to a float:

float_of_string "3.14"

join

split

character type

character literal

length

How to get the length of a string.

index of substring

How to get the index of a substring.

extract substring

How to extract a substring.

extract character

How to get the character at a specified index of a string.

The syntax for a character literal.

chr and ord

How to convert a character to its ASCII code or Unicode point; how to convert an ASCII code or Unicode point to a character.

Dates and Time

Arrays

Lists

list literal

list element element

list head

f#:

Supports List.hd (with a warning) to be compatible with OCaml.

list-tail

Supports List.tl (with a warning) to be compatible with OCaml.

Tuples

tuple

tuple element

Functions

function

How to define a function.

lambda

How to define an anonymous function.

piecewise defined function

How to define a function with multiple equations and matching on the arguments.

recursive function

How to define a recursive function.

mutually recursive functions

How to define two functions which call each other. Mutual recursion can be eliminated by inlining the second function inside the first function. The first function is then recursive and can be defined independently of the second function.

named parameter

How to define and invoke a function with named parameters.

ocaml:

Multiple parameters can share a name. In the function definition colons are used to rename the parameters for use in the function body.

let add_xs ~x:x1 ~x:x2 = x1 + x2;;
add_xs ~x:3 ~x:7;;

named parameter default value

How to make named parameters optional by providing a default value in the definition.

ocaml:

For a named parameter to be optional, it must be following by an unnamed parameter in the definition. This permits the parser to unambiguously determine if the optional parameter has been provided or not. If the optional parameter is not followed by an unnamed parameter in the definition, then named parameter is not optional. If the function is invoked without the parameter, it returns a curried version of the function which expects the missing named parameter as an argument.

infix operator in prefix position

How to invoke an infix operator in prefix position.

function in infix position

How to invoke a function in infix position.

currying

How to create a curried function by providing values for some of the arguments of a function.

function composition operator

An operator which takes two functions as arguments and returns a function constructed from them by composition.

lazy evaluation

How to evaluate the arguments to a function in a lazy manner.

Lazy evaluation is also called call-by-name.

ocaml:

OCaml provides the lazy function. It is up to the caller to specify that the argument is to evaluated lazily.

haskell:

Haskell evaluates arguments lazily by default.

strict evaluation

How to evaluate arguments before they are passed to a function.

Strict evaluation is also called call by-value.

haskell:

The seq function evaluates its first argument and then returns the second argument.

Execution Control

if

if else-if else

sequencing

while

ocaml:

There is no break or continue statement. In addition to using references, it is possible to use exceptions to break out of a while loop.

for

How to loop over a range of integers.

sml:

How to define a for loop in SML:

datatype for = to of int * int
             | downto of int * int

infix to downto

val for =
    fn lo to up =>
       (fn f => let fun loop lo = if lo > up then ()
                                  else (f lo; loop (lo+1))
                in loop lo end)
     | up downto lo =>
       (fn f => let fun loop up = if up < lo then ()
                                  else (f up; loop (up-1))
                in loop up end)

How to use the for loop:

for (1 to 9)
    (fn i => print (Int.toString i))

for (9 downto 1)
    (fn i => print (Int.toString i))

for in reverse

How to iterate over a reversed range of integers.

list iteration

How to iterate over the members of a list.

loop

An infinite loop.

Exceptions

raise error

How to raise an error.

handle error

How to handle an error.

Concurrency

Filehandles

Files

Directories

Processes and Environment

Libraries and Namespaces

namespace example

namespaces

file name restrictions

import

namespace creation

namespace alias

namespace separator

subnamespace

inspect namespace

User-Defined Types

keywords used to define types by language
pascal c c++ ocaml scala haskell
type synonym type typedef typedef type type type
sum type type enum or union type abstract class data
tuple product type type data
record product type record struct struct or class type of class data

Examples of algebraic sum types are the enumerated type of Pascal and the enum of C. The definition of the type lists a set of values which variables which have the type can contain. The values are called variants.

The enumerated type of Pascal and the enum of C are implemented as integers, and one can recover the underlying integer value associated with each variant. In Pascal one uses the ord function to do this. One can use the equality test operator to determine whether two variables hold the same variant. One can also use the less than (<) operator to determine if a variable holds a variant which occurs earlier in the type definition list than another.

An enumerated type is thus similar to defining a sequence of integer constants like this:

  typedef int month;

  const month JANUARY = 1;
  const month FEBRUARY = 2;
  .
  .
  .
  const month DECEMBER = 12;

An enumerated type gives the compiler the ability to ensure that only variants listed in the type definition list are actually stored in variables with the enumerated type however.

BETTER EXPLANATION AND MOTIVATION OF UNARY TYPES. OTHER THAN VARIANTS: UNIT. ARE
UNARY TYPES USEFUL?

Algebraic sum types are more general than enumerated types, because the variants are not restricted to being unary types. By a unary type we mean a type whose variables can only contain a single value. EXAMPLE OF SUCH AND ALGEBRAIC SUM TYPE. Because of this generality, one cannot assume that a general algebraic sum type variant has an integer representation. Some languages nevertheless define an order on the variants.

SUM TYPE: NUMBER OF VALUES IS THE SUM OF THE VALUES OF EACH OF THE VARIANTS

C UNION TYPE AS ALGEBRAIC SUM TYPE

Examples of algebraic product types are the record of Pascal and the struct of C. An algebraic product type wraps several values into a single "super" value. The components of an algebraic product type are called fields, and each has a type which constrains the values which can be stored in it. The type of each field is normally a pre-existing type, but see the note on recursive types below.

To extract a field from a product value, each field must be identified. In the case of the Pascal and the C struct the fields are given names. Product types can also be defined in which the fields are identified by position like a tuple. OCaml and Haskell support both types of product type.

Since OCaml and Haskell have both tuples and tuple product types, it is worthwhile to consider the differences. One could represent represent coordinates on plane with a simple pair tuple with this type:

  (float, float)

However, all 2-tuples in which the components are both floats are the same type. With tuple product types, we could define two distinct types:

  type cartesian = Cartestion of float * float;
  type polar = Polar of float * float;

The compiler will now prevent us from using cartesian coordinates in a place where polar coordinates are expected.

It is also instructive to consider the difference between a type synonym and a product type with a single field. In the former case the two types are interchangeable. Type synonyms are useful as a shorthand for a long type, such as a 10-tuple or a function type. Functions which operate on variables of the original type will also operate on variables with the type synonym. In fact, it should be noted that type synonyms don't create a constructor, so the constructor for the original type must be used.

A product type with a single field creates a new type and provides a constructor for it which accepts the original type as an argument. Functions which take the original type as an argument cannot be used on the new type.

COMBINED ALGEBRAIC TYPES.

Algebraic product types first appeared in 1966 in Algol W. Algol W extended Algol 60 by adding a record type. The idea was due to Niklaus Wirth and C. A. R. Hoare. Pascal, which appeared in 1970, had both a record type and an enumerated type as already noted, and the Pascal enumerated type seems to be the first example of a type that could be called an algebraic sum type.

Algebraic types first appeared in their full generality in the programming language called Hope, circa 1980. Algebraic types were soon borrowed into ML. Hope introduced the terms algebraic data type, product type, and sum type. It also introduced pattern matching.

PATTERN MATCHING.

type synonym

sum type

generic type

recursive type

Objects

Inheritance and Polymorphism

REPL

repl

repl limitations

repl last value

help

ocaml

The OCaml top level provides these directives:

#cd "DIRNAME";;
#directory "DIRNAME";;
#install_printer PRINTER_NAME;;
#label BOOL;;
#load "FILENAME";;
#print_depth N;;
#print_length N;;
#quit;;
#remove_printer PRINTER_NAME;;
#trace FUNCTION_NAME;;
#untrace FUNCTION_NAME;;
#untrace_all;;
#use "FILENAME";;
#warnings "WARNINGS_LIST";;

inspect type

load source file

search path

set search path on command line

SML

Programming in Standard ML '97
The Standard ML Basis Library

OCaml

The Objective-Caml system

F#

F# Language Reference
F# Core Library Reference

Haskell

Haskell 2010 Language Report
Haskell Hierarchical Libraries

issue tracker | content of this page licensed under creative commons attribution-sharealike 3.0