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