Languages with Type Inference: OCaml, Scala, Haskell

a side-by-side reference sheet

grammar and invocation | variables and expressions | arithmetic and logic | strings | parsing | dates and time | lists
tuples | algebraic data types | functions | polymorphism | execution control | file handles | files | directories
processes and environment | libraries and namespaces | repl | contact

ocaml scala haskell
version used
 
3.11 2.8 7.0
show version
 
$ ocaml -version $ scala -version $ ghc --version
grammar and invocation
ocaml scala haskell
interpreter
 
$ ocaml foo.ml $ scala foo.scala $ runghc foo.hs
shebang #!/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 ; )
begin expr ; end
{ } offside rule or { }
to-end-of-line comment none
 
F#:
// comment
// comment -- comment
multi-line comment (* comment
another comment
*)
/* comment
another comment
*/
{- comment
another comment
-}
hello world print_string "hello world\n";;
print_endline "hello world";;
F#:
printfn "hello world";;
println("hello world") main = putStrLn "hello world"
variables and expressions
ocaml scala haskell
write-once variable
 
let a = 3;; val a = 3 a = 3
modifiable variable let a = ref 3;;
a := 4;;
!a + 7;;
var a = 3
a = 4
a + 7
a <- return 3
unit type and value unit
()
Unit
()
()
()
conditional expression 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;;
expression has type Any:
if (true) { "hello" } else { 3 }
compilation error:
if True then "hello" else 3
null
 
None null Nothing
expression type declaration 1: Double 1 :: Double
let ... in ... 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 z = x * y
  where x = 3.0
        y = 2.0 * x
arithmetic and logic
ocaml scala haskell
boolean type
 
bool Boolean Bool
true and false
 
true false true false True False
logical operators && || not && || ! && || not
relational operators = <> < > <= >= == != < > <= >= == /= < > <= >=
min and max min 1 2
max 1 2
math.min 1 2
math.max 1 2
min 1 2
max 1 2
integer type int
other integer types:
int32 int64 nativeint
types of numeric literals:
Int
other integer types:
Byte Short BigInt
Integer
negative integer literal -4 -4 an expression, not a literal:
-4
float type float Double Float
numeric literals are Double
Double
integer operators + - * / mod
mod is an infix operator. F# uses % instead of mod
+ - * / % + - * div rem
div and rem are functions, not infix operators
float operators +. -. *. /.

F#:
+ - * /
+ - * / + - * /
add integer and float float 3 +. 7.0;; 3 + 7.0 3 + 7.0
integer division, remainder, float division 7 / 3
7 mod 3
float 7 /. float 3
7 / 3
7 % 3
(7: Double) / 3
div 7 3
rem 7 3
7 / 3
integer division by zero raises Division_by_zero

F#:
System.DivideByZeroException
java.lang.ArithmeticException Exception: divide by zero
float division by zero evaluates to infinity or neg_infinity evaluates to Infinity or -Infinity, values which do not have literals evaluates to Infinity or -Infinity, values which do not have literals
power 2.0 ** 32.0;; math.pow(2, 32) 2 ** 32

-- syntax error if exponent not an integer:
2 ^ 32
sqrt sqrt 2.0 math.sqrt(2) sqrt 2
sqrt -2.0 sqrt (-2.0) evaluates to nan math.sqrt(-2.0) evalutes to NaN, a value which has no literal sqrt (-2.0) evalutes to NaN, a value which has no literal
transcendental functions exp log
sin cos tan
asin acos atan atan2
math.exp math.log
math.sin math.cos math.tan
math.asin math.acos math.atan math.atan2
exp log
sin cos tan
asin acos atan atan2
float truncation none
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
integer overflow modular arithmetic modular arithmetic for all types except BigInt has arbitrary length integers
float overflow evaluates to infinity evaluates to Infinity, a value which has no literal evaluates to Infinity, a value which has no literal
random number
uniform int, uniform 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
ocaml scala haskell
literal "Hello, World!" "Hello, World!" "Hello, World!"
string and char types string char java.lang.String Char String Char
string literal escapes \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!"
F#:
"Hello" + ", " + "World!"
"Hello" + ", " + "World!" "Hello" ++ ", " ++ "World!"
string to number 7 + int_of_string "12"
73.9 +. float_of_string ".037"
7 + "12".toInt
73.9 + ".037".toFloat
raises NumberFormatException if string doesn't completely parse
7 + (read "12")::Integer
73.9 + (read "0.037")::Double
raises exception if string doesn't completely parse
number to string "two: " ^ string_of_int 2
"pi: " ^ float_of_string 3.14
"two: " + 2.toString
"pi: " + 3.14.toString
"two: " ++ (show 2)
"pi: " ++ (show 3.14)
length String.length "hello"
F#:
"hello".Length
"hello".length length "hello"
index of substring "hello".indexOf("hell")
extract substring String.sub "hello" 0 4 "hello".substring(0,4) drop 0 (take 4 "hello")
case manipulation String.uppercase "hello"
String.lowercase "HELLO"
String.capitalize "hello"
"hello".toUpperCase
"HELLO".toLowerCase
"hello".capitalize
import Data.Char

map toUpper "hello"
map toLower "HELLO"
character access "hello".[0] "hello"(0) "hello" !! 0
character literal 'h' 'h' 'h'
chr and ord Char.code 'a'
Char.chr 97
'a'.toInt
97.toChar
Char.ord 'a'
Char.chr 97
parsing
ocaml scala haskell
dates and time
ocaml scala haskell
date and time types ClockTime CalendarTime TimeDiff
current date and time import Time

t <- getClockTime
current unix epoch import System.Time

getClockTime >>= (\(TOD sec _) -> return sec)
lists
ocaml scala haskell
list literal [1;2;3] List(1,2,3) [1,2,3]
list element access List.nth [1;2;3] 0 List(1,2,3)(0) [1,2,3] !! 0
list head 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]
F#:
List.tail [1;2;3]
List(1,2,3).tail tail [1,2,3]
cons 1::[2;3] 1 :: List(2,3) 1 : [2,3]
reverse 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]
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(List(1,2), List(3,4)).flatten concat [[1,2], [3,4]]
length List.length [1;2;3] List(1,2,3).length length [1,2,3]
each 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 (( * ) 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 ((<) 2) [1;2;3];; List(1,2,3).filter(x=>x>2) filter (\x -> x > 2) [1,2,3]
fold left 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.fold_right (-) [1;2;3] 0;; List(1,2,3).foldRight(0)(_-_) foldr (-) 0 [1,2,3]
tuples
ocaml scala haskell
tuple (1,"hello",true) (1,"hello",true) (1,"hello",True)
tuple element access match (1,"hello",true) with _,x,_ -> x (1,"hello",true)._1 (\(a, _, _) -> a) (1,"hello",True)
pair element access fst (12, "December")
snd (12, "December")
(12, "December")._1
(12, "December")._2
fst (12, "December")
snd (12, "December")
algebraic data types
ocaml scala haskell
algebraic sum type 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} 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"} 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 constructor with argument 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 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 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 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 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 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 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"
option type 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)]
extract option type 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
functions
ocaml scala haskell
definition 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
application -- 4.5:
average 1 2 + 3

-- 3.0:
average 1 $ 2 + 3
lambda 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 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 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
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);;
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 ( * ) 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 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 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!")
polymorphism
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
execution control
ocaml scala haskell
if if x > 0 then
  print_string "pos\n";;
if ( x > 0 )
  println("pos")
if x > 0
  then putStrLn "pos"
  else return ()
if else-if else 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
}
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)
for in reverse for i = 10 downto 1 do
  let s = string_of_int i in
  print_string (s ^ "\n")
done;;
none
list iteration none for (i <- List.range(1, 11).reverse)
  println(i)
loop let rec loop i =
  if i <= 10 then begin
    print_endline (string_of_int i);
    loop (i+1)
  end in
loop 0;;
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)
file handles
ocaml scala haskell
standard file handles import System.Posix.IO

stdInput stdOutput stdError
read line from stdin val line = readLine() line <- getLine
end-of-file behavior when last data is returned, hIsEOF will return True. Reading after end-of-file throws an exception.
chomp
write line to stdout println("lorem ipsum") putStrLn "lorem ipsum"
write formatted string to stdout
open file for reading import scala.io.Source

val path = "/etc/hosts"
val f = Source.fromFile(path)
import System.IO

f <- openFile "/etc/hosts" ReadMode
open file for writing import System.IO

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

f <- openFile "/tmp/err.log" AppendMode
close file import scala.io.Source

f.close
import System.IO

hClose f
i/o errors
read line 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
iterate over file by line
read file into array of strings
read file into string
write string
write line open Printf
let oc = open_out "/tmp/test-ocaml" in
fprintf oc "hello out\n";
close_out oc;;
val out = new java.io.FileWriter("/tmp/test-scala")
out.write("hello out\n")
out.close
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
ocaml scala haskell
file test, regular file test import System

Directory.doesFileExist "/etc/hosts"

import Control.Monad
import System.Posix.Files

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

liftM fileSize (getFileStatus "/etc/hosts")
is file readable, writable, executable import Control.Monad

liftM readable
  (getPermissions "/etc/hosts")
liftM writable
  (getPermissions "/etc/hosts")
liftM executable
  (getPermissions "/etc/hosts")
set file permissions 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 import System.Directory

copyFile "/tmp/foo" "/tmp/bar"
removeFile "/tmp/foo"
renameFile "/tmp/bar" "/tmp/foo"
create symlink, symlink test, readlink import System.Posix.Files

createSymbolicLink "/etc/hosts" "/tmp/hosts"
??
readSymbolicLink "/tmp/hosts"
temporary file
directories
ocaml scala haskell
build pathname import System.FilePath ((</>))

let path = "/etc" </> "hosts"
dirname and basename import System.FilePath

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

-- returns IO [FilePath]
Directory.getDirectoryContents "/etc"
make directory import System.Directory

createDirectoryIfMissing True
  "/tmp/foo/bar"
remove empty directory 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
ocaml scala haskell
command line arguments 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
program name
 
import System

s <- getProgName
getopt
get and set environment variable
 
import System.Posix.Env

s <- getEnv "HOME"
putEnv "PATH=/bin"
get pid, parent pid import System.Posix.Process

pid <- getProcessID
ppid <- getParentProcessID
get user id and name import System.Posix.User

uid <- getRealUserID
username <- getLoginName
exit
 
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
ocaml scala haskell
namespace 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
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;;
inspect namespace module M = List;;
package manager
search; install; list installed
$ cabal list parsec
$ cabal install parsec
$ cabal list --installed
repl
ocaml scala haskell
repl $ 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 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";; :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.

show version

How to get the version.

Grammar and Invocation

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

How to define a block of statements.

to-end-of-line comment

A comment terminated by the end of the line.

delimited comment

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

ocaml:

(* *) style comments can be nested.

hello world

A "Hello, World" example.

scala

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")
  }
}

Variables and Expressions

write-once value

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.

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 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.

Arithmetic and Logic

true and false

The literals for true and false.

boolean type

The type for boolean values.

logical operators

The logical operators: and, or, and not.

relational operators

sml:

Comparisons between reals with = and <> are not permitted in SML/NJ, but are permitted in Alice.

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"

scala:

The + operator will convert a numeric type to a string if the other operand is a string. Hence the following works:

"value: " + 8

number to string

numeric types

The most commonly used 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

The 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;;

integer overflow

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

scala:

The largest integers are available in the constants Int.MaxValue and Long.MaxValue.

integer division by zero

The result of dividing an integer by zero.

integer division, remainder, float division

How to find the quotient of two integers; how to find the remainder of two integers; how to perform float division on two integers.

negative integer literal

The syntax for a negative integer literal.

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 operators

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

float exponentiation

How to exponentiate a float.

float functions

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

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.

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))

min and max

The binary functions min and max.

float overflow

The result of float overflow.

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

float division by zero

The result of division by zero.

sqrt -2.0

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

random integer, uniform float, normal float

How to generate a uniformly distributed random integer; how to generate a uniformly distributed float; how to generate a normally distributed 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

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.

Strings

literal

The syntax for a string literal.

string and char types

The types for strings and characters.

string literal 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=UTF-8

concatenation

How to concatenate strings.

f#:

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

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.

case manipulation

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

character access

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

character literal

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.

Parsing

Dates and Time

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

Algebraic Data Types

algebraic sum type

algebraic product type

type synonym

option type

Functions

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.

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.

Polymorphism

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.

raise error

How to raise an error.

handle error

How to handle an error.

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

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

OCaml

The Objective-Caml 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 2010 Language Report
GHC Standard Libraries
Real World Haskell

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

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License