a sidebyside reference sheet
arithmetic and logic  strings  lists and tuples  data types  functions and scope  execution control  environment and i/o
modules  objects  repl  contact
standard ml (1990)  ocaml (1996)  scala (2003)  haskell (1990)  

version used  SML NJ 110.71  3.11.0  2.8  6.12.1 
version  SML NJ displays version on startup  $ ocaml version  $ scala version  $ ghc version 
interpreter  $ ocaml foo.ml  $ scala foo.scala  $ runghc foo.hs  
shebang  none  #!/usr/bin/env ocaml  #!/bin/sh exec scala $0 $@ !# 
#!/usr/bin/env runghc 
bytecode compiler and interpreter  $ ocamlc foo.ml o foo $ ocamlrun foo 
$ scalac Foo.scala $ scala Foo 
none  
native compiler  $ ocamlopt foo.ml o foo $ ./foo 
none  $ ghc o foo foo.hs $ ./foo 

library which is always imported  Pervasives  Prelude  
statement terminator  ;  ;;  ; or sometimes newline  next line has equal or less indentation, or ; 
blocks  ( expr ; … )  ( expr ; … ) begin expr ; … end 
{ }  offside rule or { } 
single line comment  none  none F#: // comment 
// comment   comment 
multiline comment  (* comment another comment *) 
(* comment another comment *) 
/* comment another comment */ 
{ comment another comment } 
hello world  print "hello world\n";  print_string "hello world\n";; print_endline "hello world";; F#: printfn "hello world";; 
println("hello world")  main = putStrLn "hello world" 
value  val a = 3;  let a = 3;;  val a = 3  a = 3 
variable  val a = ref 3; a := 4; !a + 7; 
let a = ref 3;; a := 4;; !a + 7;; 
var a = 3 a = 4 a + 7 
a < return 3 
unit  ()  ()  ()  () 
conditional expression  val x = 3; if x < 0 then ~x else x; 
let x = 3;; if x < 0 then x else x;; 
val x = 3 if (x < 0) x else x 
x = 3 if x < 0 then x else x 
branch type mismatch  compilation error: if true then "hello" else 3; 
compilation error: if true then "hello" else 3;; 
expression has type Any: if (true) { "hello" } else { 3 } 
compilation error: if True then "hello" else 3 
null  NONE  None  null  Nothing 
type declaration  1: Double  1 :: Double  
arithmetic and logic  
standard ml  ocaml  scala  haskell  
true and false  true false  true false  true false  True False 
boolean type  bool  bool  Boolean  Bool 
logical operators  andalso orelse not  &&  not  &&  !  &&  not 
relational operators  = <> < > <= >=  = <> < > <= >=  == != < > <= >=  == /= < > <= >= 
numeric types  int real  int float other numeric types: int32 int64 nativeint 
types of numeric literals: Int Double other numeric types: Byte Short Float BigInt 
Integer Double 
integer operators  +  * div mod  +  * / mod mod is an infix operator. F# uses % instead of mod 
+  * / %  +  * div rem div and rem are functions, not infix operators 
integer exponentiation  none  none  2 ^ 3  
largest integer  int: 2^{30}1  Int: 2^{31}1 Long: 2^{63}1 BigInt: none 
none  
integer overflow  Overflow exception  modular arithmetic  modular arithmetic for all types except BigInt  no overflow 
integer division by zero  raises Division_by_zero F#: System.DivideByZeroException 
java.lang.ArithmeticException  Exception: divide by zero  
integer division, remainder, float division  7 div 3 7 mod 3 real 7 / real 3 
7 / 3 7 mod 3 float 7 /. float 3 
7 / 3 7 % 3 (7: Double) / 3 
div 7 3 rem 7 3 7 / 3 
negative integer literal  ~4  4  4  4 
float operators  +  * /  +. . *. /. F#: +  * / 
+  * /  +  * / 
float exponentiation  Math.pow (2.0,3.0);  2.0 ** 3.0;;  math.pow(2.0,3.0) scala 2.7: Math.pow(2.0,3.0) 
2.0 ** 3.0 
float functions  sqrt exp log sin cos tan asin acos atan atan2  math.sqrt math.exp math.log math.sin math.cos math.tan math.asin math.acos math.atan math.atan2 use Math in scala 2.7 
sqrt exp log sin cos tan asin acos atan atan2  
add integer and float  real 3 + 7.0;  float 3 +. 7.0;;  3 + 7.0  3 + 7.0 
arithmetic truncation  round 3.14 trunc 3.14 floor 3.14 ceil 3.14 
?? truncate 3.14 floor 3.14 returns float ceil 3.14 returns float F# has round 
3.14.round ?? 3.14.floor returns Double 3.14.ceil returns Double 
round 3.14 truncate 3.14 floor 3.14 ceiling 3.14 
min and max  min 1 2 max 1 2 
math.min 1 2 math.max 1 2 use Math in scala 2.7 
min 1 2 max 1 2 

float overflow  infinity  Infinity  Infinity  
float division by zero  infinity or neg_infinity  Infinity or Infinity  Infinity or Infinity  
sqrt 2.0  Math.sqrt ~2.0: nan  sqrt (2.0): nan  math.sqrt(2.0): NaN  sqrt (2.0): NaN 
random integer, random float, normal float  Random.int 100 Random.float 1.0 none 
val r = new java.util.Random r.nextInt(100) r.nextDouble r.nextGaussian 
import System.Random getStdRandom (randomR (0, 99)) getStdRandom (randomR (0.0, 1.0)) none 

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 

strings  
standard ml  ocaml  scala  haskell  
literal  "Hello, World!"  "Hello, World!"  "Hello, World!"  "Hello, World!" 
string and char types  string char  string char  java.lang.String Char  String Char 
string escapes  \000 \a \b \f \n \r \t \v \040  \b \n \r \t \" \' \\ \040 \x7F  \b \f \n \r \t \" \' \uhhhh \o \oo \ooo  \0 \a \b \f \n \r \t \v \" \& \' \\ \x3bb \955 
concatenation  "Hello" ^ ", " ^ "World!"  "Hello" ^ ", " ^ "World!" F#: "Hello" + ", " + "World!" 
"Hello" + ", " + "World!"  "Hello" ++ ", " ++ "World!" 
length  size "hello"  String.length "hello" F#: "hello".Length 
"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") 
number to string  Int.toString 3 Real.toString 3.14 
string_of int 3 string_of_float 3.14 
3.toString 3.14.toString 
show 3 show 3.14 
string to number  int_of_string "3" float_of_string "3.14" 
"3".toInt "3.14".toFloat raises NumberFormatException if string doesn't completely parse 
read "3"::Integer read "3.14"::Double raises exception if string doesn't completely parse 

case manipulation  String.uppercase "hello" String.lowercase "HELLO" String.capitalize "hello" 
"hello".toUpperCase "HELLO".toLowerCase "hello".capitalize 

character access  String.sub ("hello",0)  "hello".[0]  "hello"(0)  "hello" !! 0 
character literal  #"h"  'h'  'h'  'h' 
chr and ord  ord #"a" chr 97 
Char.code 'a' Char.chr 97 
'a'.toInt 97.toChar 
Char.ord 'a' Char.chr 97 
lists and tuples  
standard ml  ocaml  scala  haskell  
list literal  [1,2,3]  [1;2;3]  List(1,2,3)  [1,2,3] 
list element access  List.nth ([1,2,3],0)  List.nth [1;2;3] 0  List(1,2,3)(0)  [1,2,3] !! 0 
list head  List.hd [1,2,3]  List.hd [1;2;3] F#: List.head [1;2;3] 
List(1,2,3).head  head [1,2,3] 
list tail  List.tl [1,2,3]  List.tl [1;2;3] F#: List.tail [1;2;3] 
List(1,2,3).tail  tail [1,2,3] 
cons  1::[2,3]  1::[2;3]  1 :: List(2,3)  1 : [2,3] 
reverse  List.rev [1,2,3]  List.rev [1;2;3]  List(1,2,3).reverse  reverse [1,2,3] 
sort  List.sort min [1;3;2;4] List.sort max [1;3;2;4] 
List(1,3,2,4).sort((x,y)=>x<y) List(1,3,2,4).sort(_<_) List(1,3,2,4).sort((x,y)=>x>y) List(1,3,2,4).sort(_>_) 
List.sort [1,3,2,4]  
append  [1,2] @ [3,4]  [1;2] @ [3;4] List.append [1;2] [3;4] 
List(1,2) ::: List(3,4) List(1,2) ++ List(3,4) 
[1,2] ++ [3,4] 
concatenate list of lists  List.concat [[1,2],[3,4]]  List.concat [[1;2];[3;4]]  List(List(1,2), List(3,4)).flatten  
length  List.length [1,2,3]  List.length [1;2;3]  List(1,2,3).length  length [1,2,3] 
each  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];; 
List(1,2,3).foreach(i=>println(i))  mapM_ print [1,2,3] 
map  List.map (fn (x) => x + 2) [1,2,3];  List.map (( * ) 2) [1;2;3];;  List(1,2,3).map(x=>2*x) List(1,2,3).map[Int](2*_) 
map (\x > x * x) [1,2,3] 
filter  List.filter (fn (x) => x > 2) [1,2,3];  List.filter ((<) 2) [1;2;3];;  List(1,2,3).filter(x=>x>2)  filter (\x > x > 2) [1,2,3] 
fold left  List.foldl (op +) 0 [1,2,3];  List.fold_left (+) 0 [1;2;3];; F#: List.fold () 0 [1;2;3];; 
List(1,2,3).foldLeft(0)(_+_) List(1,2,3).foldLeft(0)((x,y)=>x+y) 
foldl (+) 0 [1,2,3] 
fold right  List.foldr (op ) 0 [1,2,3];  List.fold_right () [1;2;3] 0;;  List(1,2,3).foldRight(0)(__)  foldr () 0 [1,2,3] 
tuple  (1,"hello",true)  (1,"hello",true)  (1,"hello",true)  (1,"hello",True) 
tuple element access  #1 (1,"hello",true)  match (1,"hello",true) with _,x,_ > x  (1,"hello",true)._1  (\(a, _, _) > a) (1,"hello",True) 
pair element access  #1 (12,"December") #2 (12,"December") 
fst (12, "December") snd (12, "December") 
(12, "December")._1 (12, "December")._2 
fst (12, "December") snd (12, "December") 
data types  
standard ml  ocaml  scala  haskell  
algebraic sum type  datatype color = Red  Green  Blue  type color = Red  Green  Blue  abstract class Color case object Red extends Color case object Blue extends Color case object Green extends Color 
data Color = Red  Green  Blue 
algebraic product type  type customer = {id:int, name:string, address:string}  type customer = {id:int; name:string; address:string}  case class Customer(id: Int, name: String, address: String)  data CustomerType = Customer { customerId :: Integer, name :: String, address :: String } 
algebraic product literal  {id=7, name="John", address="Topeka,KS"}  {id=7; name="John"; address="Topeka,KS"}  Customer(7,"John","Topeka,KS") scala 2.8: Customer(id=7, name="John", address="Topeka,KS") 
Customer { customerId=7, name="John", address="Topeka, KS" } 
type synonym  type name = string  type name = string  type Name = String  type Name = String 
type constructor with argument  datatype special_int = SpecialInt of int; val x = SpecialInt 7; 
type special_int = SpecialInt of int;; let x = SpecialInt 7;; 
class SpecialInt(x: Int) val x = new SpecialInt(7) 
data SpecialIntType = SpecialInt Integer x = SpecialInt 7 
type constructor with tuple argument  datatype int_pair = IntPair of int * int; val y = IntPair (7,11); 
type int_pair = IntPair of int * int;; let y = IntPair ( 7, 11 );; 
class IntPair(a: Int, b: Int) val x = new IntPair(7,11) 
data IntPairType = IntPair Integer Integer y = IntPair 7 11 
parametric type constructor  datatype ('a,'b) twosome = Twosome of 'a * 'b; val z = Twosome ("pi",3.14); 
type ('a,'b) twosome = Twosome of 'a * 'b;; let z = Twosome ("pi",3.14);; 
class Twosome[A,B](a: A, b: B) val p = new Twosome("pi",3.14) 
data TwosomeType a b = Twosome a b z = 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;;  abstract class BinaryTree case class Tree(left: BinaryTree, right: BinaryTree) extends BinaryTree case class Leaf(x: Int) extends BinaryTree 
data BinaryTree = Leaf Integer  Tree BinaryTree BinaryTree 
match/with case/of  val c = Red; case c of Red => "red"  Blue => "blue"  Green => "green"; 
let c = Red;; match c with Red > "red"  Blue > "blue"  Green > "green";; 
val c:Color = Red; c match { case Red => "red"; case Green => "green"; case Blue => "blue" } 
c = Red case c of Red > "red" Green > "green" Blue > "blue" 
match guard  none, use if  match i with j when i < 0 > j  j > j;;  match { case i: Int if i < 0 =>  i; case i: Int => i }  none, use if or piecewise function definition 
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;; 
val c : Color = Green c match { case Red => "red"; case _ => "not red" } 
c = Green case c of Red > "red"; _ > "not red" 
null 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)];; 
val list = List(Some(3),null,Some(4))  list = [Just(3),Nothing,Just(4)] 
functions and scope  
standard ml  ocaml  scala  haskell  
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;; 
val z = { val x = 3.0 val y = 2.0 * x 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 
function  fun average a b = ( a + b ) / 2.0;  let average a b = ( a +. b ) /. 2.0;;  def average(a: Double,b: Double) = (a + b) / 2.0 if body not an expression or if return type not inferable: def average(a: Double,b: Double): Double = { (a + b) / 2.0 } 
average a b = (a + b) / 2.0 
lambda  fn x => fn y => (x+y) / 2.0  fun x > fun y > (x +. y) /. 2.0  (x:Double, y:Double) => (x+y) / 2.0  \x y > (x+y) / 2.0 
piecewise defined function  val to_s = fn Red => "red"  Green => "green"  Blue => "blue"; 
let to_s = function Red > "red"  Green > "green"  Blue > "blue";; 
none  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;; 
def range(a:Int, b:Int): List[Int] = if (a > b) List() else a :: range(a+1,b)  range a b = if a > b then [] else a : range (a+1) b 
mutuallyrecursivefunctions  let rec even n = if n = 0 then true else odd (n1) and odd n = if n = 0 then false else even (n1);; 

named parameter  let subtract ~m ~s = m  s;; subtract ~s: 3 ~m: 7;; 
scala 2.8: def subtract(m: Int, s: Int) = 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;; 
scala 2.8: def logarithm( x: Double, base: Double = math.exp(1)) = math.log(x) / math.log(base) logarithm(2.718) logarithm(10, base=2) 
none  
infix operator in prefix position  (op * ) (3,4)  ( * ) 3 4;;  none  ( * ) 3 4 
function in infix position  none  unary methods can be used as binary operators  add x y = x + y 3 `add` 4 

currying  fun plus x y = x + y; val plus2 = plus 2; plus2 7; 
let plus2 = (+) 2;;  def plus(x: Int)(y: Int) = x + y plus(3)(7) def plus2 = plus(2) _ plus2(7) 
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  none  double x = 2 * x quadruple x = double . double 
lazy evaluation  let first_arg x y = x;; first_arg 7 (lazy (1/0) );; 
def first_arg(x: => Double, y: => Double): Double = x first_arg(7, 1/0) 
lazy evaluation is default: first_arg x y = x first_arg 7 (error "bam!") 

strict evaluation  first_arg x y = seq y x first_arg 7 (error "bam!") 

execution control  
standard ml  ocaml  scala  haskell  
if  if x > 0 then print "pos\n" else (); 
if x > 0 then print_string "pos\n";; 
if ( x > 0 ) println("pos") 
if x > 0 then putStrLn "pos" else return () 
if elseif else  if x > 0 then print "pos" else if x < 0 then print "neg" else print "zero";  if x > 0 then print_string "pos" else if x < 0 then print_string "neg" else print_string "zero";; 
if (x > 0) println("pos") else if (x < 0) println("neg") else println("zero") 
if x > 0 then putStrLn "pos" else if x < 0 then putStrLn "neg" else putStrLn "zero" 
sequencing  print_string "one\n"; print_string "two\n"; print_string "three\n" 
println("one") println("two") println("three") 
do putStrLn "one" putStrLn "two" putStrLn "three" 

while  let i = ref 0;; while !i < 10 do print_string (string_of_int !i); i := !i + 1 done;; 
var i = 0 while (i<10) { printf("%d\n", i) i = i+1 } 
none  
for  for i = 1 to 10 do let s = string_of_int i in print_string (s ^ "\n") done;; 
for (i < 1 to 10) println(i) 
none  
for in reverse  for i = 10 downto 1 do let s = string_of_int i in print_string (s ^ "\n") done;; 
none  none  
list iteration  none  for (i < List.range(1, 11).reverse) println(i) 
none  
loop  let rec loop i = if i <= 10 then begin print_endline (string_of_int i); loop (i+1) end in loop 0;; 
none  none  
raise error  raise (Failure "bam!");; or failwith "bam!";; 
throw new Exception("bam!")  error "bam!"  
handle error  let x = try 1 / 0 with Division_by_zero > 0;;  import java.lang._ val x = try { 1 / 0 } catch { case e: ArithmeticException => 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);;  assert(1==0)  
environment and i/o  
standard ml  ocaml  scala  haskell  
command line args  for i = 0 to Array.length Sys.argv  1 do print_endline i Sys.argv.(i) done 
object Test { def main(args: Array[String]) { for (arg < args) println(arg) } } 
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 

read from file  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 scala.io.Source val src = Source.fromFile("/etc/passwd") for (line < src.getLines) print(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 
write to file  val file = "/tmp/testsml"; val f = TextIO.openOut file; TextIO.output(f, "hello out\n"); TextIO.closeOut f; 
open Printf let oc = open_out "/tmp/testocaml" in fprintf oc "hello out\n"; close_out oc;; 
val out = new java.io.FileWriter("/tmp/testscala") out.write("hello out\n") out.close 
s = "hello out\n" f = "/tmp/testhaskell" main = writeFile f s 
modules  
standard ml  ocaml  scala  haskell  
module example  Baz.scala package Foo.Bar; class Baz { def say() { println("hello"); } } Main.scala import Foo.Bar.Baz; object Main { def main(args : Array[String]) { val baz = new Baz; baz.say(); } } to compile and run $ scalac Baz.scala $ scalac Main.scala $ scala Main hello 
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  none  module Foo.Bar must be in Foo/Bar.hs  
import  open Graphics;;  import Data.Bytestring  
module creation  put code in file MODULE_NAME.ml  
module alias  module Gr = Graphics;;  import qualified Data.Bytestring as B  
module separator  .  .  
submodule  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;; 

inspect module  module M = List;;  
objects  
standard ml  ocaml  scala  haskell  
class definition  class counter = object val mutable n = 0 method incr = n < n+1 method get = n end;; 
class Counter { private var n = 0 def incr(): Unit = { n = n+1 } def get(): Int = { n } } 

object creation  let c = new counter;;  val c = new Counter  
method invocation  c#incr;; c#get;; 
c.incr c.get 

field access  none  
inheritance  
repl  
standard ml  ocaml  scala  haskell  
repl  $ sml  $ ocaml to start F# repl, highlight code in visual studio and press ALT+ENTER 
$ scala  $ 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 F#: it 
res0, res1, …  it 
help  none  :help  :?  
inspect type  repl displays the type of any expression entered  repl displays the type of any expression entered  let a = 3 :type a 

load source file  use "hello.ml";  #use "hello";;  :edit hello.hs :load hello 

search path  #directory "libdir";;  
set search path on command line  ocaml Ilibdir  
__________________________________________  __________________________________________  __________________________________________  __________________________________________ 
version used
Versions used to test the code samples in the cheat sheet.
version
How to get the version.
interpreter
How to run the interpreter on a file of source code.
scala:
Scala can be run "Perl style" like this:
scala foo.scala
or "Java style" like this:
scala Foo
When the code is run "Java style", the code to be executed must be in the main method of an object with the same name as the file. When the code is run "Perl style" the statements o be executed should be at the top level outside of any object, class, or method.
shebang
How to use the interpreter in a shebang.
scala
To use scala as a shebang, it is necessary to terminate the shell script portion of the script with !#
#!/bin/sh
exec scala $0 $@
!#
println("hello world")
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.
scala:
Scala infers the existence of a semicolon at the end of a newline terminated line if none of the following conditions hold:
 the line ends with a infix operator, including a period
 the following line begins with a word that is not legal at the start of a statement
 the line ends inside parens or square brackets, neither of which can contain multiple statements
blocks
single line comment
multiline comment
ocaml:
(* *) style comments can be nested.
hello world
scala
In order for a scala program to be compiled and run, there must be a top level object with a main method:
object Hello {
def main(args: Array[String]) {
println("hello world")
}
}
value
variable
unit
null
Arithmetic and Logic
true and false
boolean type
logical operators
relational operators
sml:
Comparisons between reals with = and <> are not permitted in SML/NJ, but are permitted in Alice.
numeric types
scala:
Arithmetic operators can be used on values of type Char, which then behaves as a 16 bit unsigned integer. Integer literals are of type Int unless suffixed with L:
scala> 9223372036854775807L
res24: Long = 9223372036854775807
scala> 9223372036854775807
<console>:1: error: integer number too large
integer operators
integer 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;;
largest integer
scala:
The largest integers are available in the constants Int.MaxValue and Long.MaxValue.
integer overflow
integer division by zero
integer division, remainder, float division
negative integer literal
float operators
float exponentiation
float functions
add integer and float
ocaml:
OCaml also can convert a integer to float with float_of_int.
arithmetic truncation
min and max
float overflow
float division by zero
sqrt 2.0
random integer, uniform float, normal float
scala:
The scala native math (named Math before Scala 2.8) library has a function for returning a random double in uniform distribution on the unit interval:
math.random
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.
Strings
literal
concatenation
f#:
F# supports (with a warning) the ^ operator for compatibility with OCaml.
string escapes
scala:
Unicode escapes might not work when scala is installed on a Mac because the encoding is set by default to MacRoman:
scala> System.getProperty("file.encoding")
res0: java.lang.String = MacRoman
This can be fixed by passing the following flag to java in the scala shell script:
Dfile.encoding=UTF8
Lists and Tuples
list literal
list element element
list head
f#:
Supports List.hd (with a warning) to be compatible with OCaml.
listtail
Supports List.tl (with a warning) to be compatible with OCaml.
tuple
tuple element
Data Types
algebraic sum type
algebraic product type
type synonym
Functions and Scope
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 defintiions are performed in parallel, so later definitions cannot use the earlier definitions:
let z =
let x = 3
and y = 4 in
x * y;;
scala:
Blocks can be used in Scala exclusively to define scope. Furthermore blocks are expressions and evaluate to their last statement.
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.
function
How to define a function.
scala
Recursive functions must have their return type declared because the Scala compiler cannot infer it.
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.
scala:
Functions can only be curried if they are defined with special syntax. Functions defined with this syntax must be invoked with a pair of parens for each argument.
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.
standard ml:
Alice provides the lazy function. It is up to the caller to specify that the argument is to be evaluated lazily.
ocaml:
OCaml provides the lazy function. It is up to the caller to specify that the argument is to evaluated lazily.
scala:
Functions can be defined to evaluate their arugments lazily by putting a => operator between the colon and the type of the parameter in the function signature.
haskell:
Haskell evaluates arguments lazily by default.
strict evaluation
haskell:
The seq function evaluates its first argument and then returns the second argument.
Execution Control
if
if elseif 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 (up1))
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.
raise error
How to raise an error.
handle error
How to handle an error.
Environment and I/O
Modules
module example
namespaces
file name restrictions
import
module creation
module alias
module separator
submodule
inspect module
Objects
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
Standard ML Basis Library
Notes on Programmming in SML/NJ (pdf)
Syntax Across Languages: SML
MLton Documentation
Alice Homepage
OCaml
The ObjectiveCaml system, release 3.11
Objective CAML Tutorial
Introduction to Objective Caml (pdf) Hickey
Standard ML and Objective Caml, Side by Side
Caml Programming Guidelines
Syntax Across Languages: OCaml
The F# Survival Guide
Scala
The Scala Language Specification (pdf)
Scala 2.7.7 API
Java 1.6 API
Haskell
Haskell 98 Language and Libraries
What follows is an explanation of traits which make Haskell different from OCaml and Scala.
lazy evaluation of arguments by default
In Haskell as in most languages it is possible to provide expressions as arguments to a function when it is invoked. In most other languages the expressions are evaluated before the invoked function is executed. In Haskell the expressions are wrapped in closures called thunks and passed to the executed function which evaluates them as needed. As a result the following Haskell code runs and evaluates to 7, whereas the equivalent code in most other language would raise a division by zero error:
first_arg x y = x
first_arg 7 (1/0)
In Lisp terminology, a functions which evaluates its arguments lazily is called an expr or a fexpr. A function which performs strict evaluation is called a subr or a fsubr. exprs and fexprs were used in old Lisp dialects to implement conditional expressions such as if and cond. The terminology is still encoutered, though in modern Lisp dialects if and cond are implemented with macros.
type system identifies pure functions
The Haskell type system distinguishes between pure and impure functions. Impure functions are called actions and usually have a type of the form IO t where t is a type variable. The main routine has type IO ().
no loops
OCaml, Scala, and Haskell all perform tail call optimization so that loops can be replaced with equally efficient code that performs recursion. Actually, in the case of Haskell, loops must be replaced with recursion because Haskell doesn't provide any functions or keywords to implement loops.
indentation defines blocks
main function marks initial execution point
History
Lambda Calculus
An Introduction to Lambda Calculus (pdf) Barendregt 1994
Developed by Alonzo Church in the 1930s, the lambda calculus is a model of computation. Church used it in 1936 to show that arithmetic as formalized by a Peano system is undecidable; that is, there are theorems which are true, but which cannot be shown to be true using the axioms of arithmetic.
As a model of computation, the lambda calculus is equal in expressive power to Turing machines. No computational models have been discovered which have greater expressive power than the lambda calculus or Turing machines. The unprovable ChurchTuring thesis states that there are none.
Statements in the lambda calculus are built up with two operations: abstraction and application. Abstraction can be interpreted as defining a mathematical function, and application can be interpreted as use of a mathematical function.
Turing machines, in contrast, consist of a tape, a head, a table of instructions, and a state register. These concepts correspond to the memory, the address of the next instruction to execute, the set of machine instructions, and the registers of a computer with the von Neumann architecture.
The lambda caculus can treat functions as data. Indeed, in pure lambda calculus, functions are the only kind of data there is. The lambda calculus can nevertheless model the natural numbers by assigning a function to represent each natural number. These functions are the Church numerals. Functions can also be assigned to represent the values true and false, and the logical operators and, or, and not; these functions are the Church booleans.
The KleeneRosser paradox, obtaining by defining k=(λ x.~(xx)) and then applying k to itself, shows that the lambda calculus cannot be used as a basis for mathematics in a manner similar to first order predicate logic and the Zermelo axioms of set theory. Typed systems of lambda calculus attempt to address this deficiency by assigning types to functions and data and forbidding application of a function to a datum if the types do not match. Although typed systems can avoid the KleeneRosser paradox, they are not as expressive as the untyped lambda calculus.
Assigning a type to a function in the lambda calculus is comparable to assigning a type to a variable or a region of memory in a C program. In the case of the C program, the type remembers that the memory contains data that should be interpreted as, say, an integer and not a floating point number or string data. Assigning types to memory thus prevents programming errors that would result in nonsense. Types can also prevent programs from being written that would crash because of incorrect use of memory containing the address of another memory location.
Typed lambda calculi are central to the CurryHoward correspondence, which is a onetoone relationship between mathematical proofs and a class of computer programs.
Functional Programming
The Next 700 Programming Languages (pdf) Landin 1966
Landin's 1966 article introduces a theoretical core for future languages called ISWIM. Languages built on this core would have a "functional" subset of features, and portions of the program written entirely in the functional subset could be reasoned about in the same manner that mathematicians reason about algebraic equations. In particular, the principle of substitution holds: when two different expressions yield the same value, one expression can be substituted for the other with no change in result.
Landin's article shows that computer scientists were already distinguishing between imperative programming and functional programming. Landin isolates two features of imperative languages which invalidate the applicability of substitution: jumps and assignment.
TODO: referential transparency. In algebraic equations. In lambda calculus. Order of calculuation not significant.
Functional programming got its start with Algol, which introduced lexical scope, and Lisp 1.5, which put an emphasis on programming with expressions instead of statements. ISWIM employs the let and where keywords for lexical scope, where differing from let in that it places the definitions after the expression that uses them. The language CPL, described in 1963 and unimplemented as of 1966, was already using where in this manner. The language BASIC developed at Dartmouth in 1964 used let for dynamic assignment. let was adoped by future dialects of Lisp.
Landin notes that many Algol programmers were making it a practice to eliminate jumps or the goto statement where possible. In 1968 Dijkstra would write an influential letter to the ACM entitled "GOTO considered harmful".
Landin uses let rec and where rec for defining recursive functions. This notation persists in OCaml but not SML. Landin also proposed using indentation, the socalled offsides rule, for defining blocks, a practice that was picked up by Haskell but not the ML dialects.
TODO: the development of Scheme
ML History
Edinburgh LCF Milner 1978
How ML Evolved (pdf) Milner 1982
Milner etal. began work on a theorem proving assistant called LCF at Stanford in 1971. Work continued at Edinburgh in 1974. Milner needed a language which supported higher order functions; i.e. functions which accept functions as values. He chose ISWIM as the basis for his language. Milner needed the language to be typed so that theorems being proved could be distinguished from the tactics for proving theorems.
Typed, higher order functions have complex declarations, and Milner developed and described Algorithm W in 1978 for the inference of type. Milner later learned that some of his results had already been arrived at by Hindley and Church.
ML was initally implemented in the Stanford dialect of Lisp. Luca Cardelli implemented a compiled version of ML in 1981. Specifically, a compiler implemented in Pascal took the ML source and converted it to an intermediate form which could be executed on an abstract machine implemented in assembler. The compiler and abstract machine were available for VAX machines running Unix. All type checks took place in the compiler.
Standard ML History
Hope: an Experimental Applicative Language (pdf) Burstall MacQueen Sannella 1980
The Definition of Standard ML (Revised) Milner etal 1997
SML '97 Conversion Guide MacQueeen 1999
The original version of ML was somewhat vaguely specified, and Cardelli's Pascal implementation introduced some changes and improvements. Furthermore, another statically typed functional language called Hope appeared in 1980 which introduced algebraic data types and pattern matching. Milner realized that ML had value independent of its role in the theorem proving assistant LCF, and that it would benefit from additions and standardization.
Hence Milner published "A Proposal for Standard ML" in December 1983. The article was followed by a three day conference in June 1984. Numerous changes were made to the language, and MacQueen designed a module system for ML. The formal description of Standard ML was published in book form in 1990 and a revision published in book form in 1997.
Polymorphism Newsletter
The Polymorphism Newsletter was edited by Cardelli and MacQueen, at the time both working at Bell Labs.
Polymorphism I.0 (pdf) Cardelli and MacQueen, Nov 1982
 Known VAXML System Locations
Polymorphism I.1 (pdf) Cardelli and MacQueen, Jan 1983
 How ML Evolved, Milner
 Unambiguous Syntax for ML, Sethi
 The Functional Abstract Machine, Cardelli
 SERC ML/LCF/Hope Meeting at Rutherford Labs
Polymorphism I.2 (pdf), Cardelli and MacQueen, Apr 1983
 Poly Report, Matthews
 Introduction to Poly, Matthews
 Ponder and its Type System, Fairbairn
 Recent Developments in LCF: Examples of Structural Induction, Paulson
 Rewriting in Cambridge LCF, Paulson
 A Revised Logic PPLambda: A Reference Manual, Paulson
 ML Under Eunice, Kitchen and Linch
Polymorphism I.3 (pdf) Cardelli and MacQueen, Dec 1983
 A Proposal for Standard ML, Milner
 ML under Unix, Cardelli
 Modules for ML, MacQueen
 Stream Input/Output, Cardelli
Polymorphism II.1 (pdf) Cardelli and MacQueen, Jan 1985
 Report on the Standard ML Meeting, MacQueen
 Basic Polymorphic Type Checking, Cardelli
 Annotated Bibliography on LCF, Paulson
Polymorphism II.2 (pdf) MacQueen, Oct 1985
 The Standard ML Core Language, Milner
 Modules for ML, MacQueen
 Standard ML Input/Output, Harper
SML Implementations
Standard ML of New Jersey
Standard ML of New Jersey
Rabbit: A Compiler for Scheme Steele 1978
Caml Trading Talk at CMU (video) Minsky 2009, opinions on SML implementations at 51:10
Compiling with Continuations Appel 1991
MacQueen and Appel started work on SMLNJ in 1986, and they spent over five years developing a complete programming environment for SML. The SMLNJ compiler reduces SML source code to an intermediate representation which uses continuation passing style (CPS). CPS was first used for a Scheme compiler written by Steele in 1978, and since CPS can be expressed in Scheme, the intermediate representation used by the Scheme compiler was a subset of Scheme. SMLNJ uses ML data types to represent CPS expressions, which provide type safety not available to a Scheme IR.
SMLNJ provides both a compiler and an interpreter. It is still actively being worked on; the version 110.72 was releases in February 2010.
MLton
MLton
MLton Performance Compared to Other ML Compilers
MLton is an SML to C compiler. Work was initiated by Stephen Weeks in 1997, and MLton was first released in 1999. MLton is a "whole program compiler". By 2002 the ability to generate native code had been added.
MLton appears to be the fastest SML compiler. The most recent release (as of May 2010) was in August 2007.
Alice
SML.NET
Moscow ML
Caml History
A brief History of Caml
Categorical Abstract Machine (CAM)
INRIA became interested in the ML language as early as 1980. The first version of Caml was released in 1987. The name refers to the Categorical Abstract Machine, a model of computation of interest to the researchers at INRIA. The first version of Caml, retroactively known as Caml Heavy, compiled to the LLM3 virtual machine used by Le Lisp. In 1991 Xavier Leroy released Caml Light which had a bytecode interpreter written in C. In 1996 OCaml was released. The name referred to the object system which was added to Caml; the release included a native code compiler.
Don Syme worked on the generics for C# which were introduced in Visual Studio 2005. F# has been available as a download since 2007 and is included with Visual Studio 2010. F# is close to OCaml; it had been preceded by an attempt to bring Haskell to .NET which was put aside because too much monadic code was required to interact with the .NET libraries. F# was also preceded by an SML port to .NET.
Scala History
The Origins of Scala Odersky 2009
Joincalculus
Scala Version History: 2.5 thru 2.7
Scala Version History: 1.0 thru 2.4
Martin Odersky and Phil Wadler developed a functional language called Pizza for the JVM in 1996. Pizza has generics — what would eventually become the generics in Java 5 — as well as higher order functions and pattern matching. Odersky next developed a JVM language called Funnel which is based on the joincalculus.
Odersky's third language was Scala; the first version was released in 2003. The Scala compiler was rewritten in Scala in 2006.
Haskell History
A History of Haskell (pdf) Hudak Hughes Jones Wadler 2007
Notions of computations and monads (pdf) Moggi 1991
Comprehending Mondas (pdf) Wadler 1992
Arrows wikipedia
David Turner implemented a pure version of ISWIM in 1972, and he made the language (called SASL) nonstrict or lazy in 1976. He followed this language up with KRC (1981) which contained pattern matching, guards, and list comprehensions. This in turn was followed by Miranda (1985), which had polymorphic types in the manner of ML.
Miranda is closed source. The FP community resolved to design an open source lazy functional language in 1987, and the new language was specified in the Haskell Report (1990) and the Haskell 98 Report (1999). The Glasgow Haskell Compiler was first available as a beta version in 1991.
A notable distinction between Miranda and Haskell is the use of category theory, in particular monads, in the type system to distinguish pure and impure functions. Arrows are a generalization of monads.