Getting started

Comments

(* This is a comment. *)
val z = 12;
val abs_of_z = if z < 0 then 0 - z else z;
 

arithmatic

val a = ~5; (* a is -5 *)
val v = x div y;

function

fun f(x1: t1, x2: t2, ... xn: tn) = e

tuple

(* tuple is a compound data structrue contains fixed size of elements with different types *)
val tuple = (e1, e2) (* with type t1 * t2, where t1 is the type of e1 and t2 is the type of e2 *)
(* access member of tuple *)
(#1 tuple)

list

(* list is a compound data structure contains arbitrary count of elements with same type*)
 
(* list construction *)
[7, 8, 9] (* with type int list *)
5::[6, 7, 8]
 
(* list accessing *)
null e (* evaluates to true if e evaluates to [] *)
hd e (* if e evaluates to [], raise exception, otherwise evaluates to the first element of e *)
tl e (* raise exemption if e evaluates to [] *)

let expression

(* let expression is used to create a scope 
	where a binding is in the environment 
	in and only in later bindings and body of the let expression*)
(* syntax: let bindings in expression end *)

options

(* t option is a type for any type t *)
(* construction *)
NONE (* hast type 'a option '*)
SOME e (* has type t option if e has type t *)
 
(* accessing *)
isSome: 'a option -> bool
valOf: 'a option -> 'a (* exception if given NONE *)

boolean operations

e1 andalso e2
e1 orelse e2
not e1
 
(* Language does not need andalse, orelse, not *)
(* e1 andalso e2 *)
if e1 then e2 else false
(* e1 orelse e2 *)
if e1 then true else e2
(* not e1 *)
if e1 then false else true
 
(* Comparisons for int values *)
(* = <> > < >= <= *)
(* > < >= <= can be used with real, but not 1 int and 1 real *)
(* = <> can by used with any "equality" type but not with real *)

records

val foo = {bar=3,baz=(1,[true,false],"world"),foo="hello"} :
  {bar:int, baz:int * bool list * string, foo:string}
 
#bar foo (* val it = 3 : int *)
 
(* tuple is just a syntatic sugar for record with particular field name *)

type binding

datatype mytype = TwoInts of int * int
	| Str of string
	| Pizza
val a = Str "hi" : mytype
val b = Str : string -> mytype
val c = Pizza : mytype
val d = TwoInts(1+2, 3+4) : mytype
 
(* case expression *)
let x = Pizza;
case x of 
	Pizza => 1
	Str s => String.size s
	TwoInts(i1, i2) => i1 + i2
	
(* redundant case arm will cause compile time error *)
(* non exausted case arm will cause a warning and throw an exception when x is case of missing arms *)
 
(* type alias *)
type foo = int

The above code:

  • adds a new type mytype to the environment.
  • adds constructoers to the environment: TwoInts, Str, Pizza
  • a constructor is a function that makes values of the new type
    • TwoInts : int * int -> mytype
    • Str : string -> mytype
    • Pizza : mytype

polymorphic data types

datatype 'a option = NONE | SOME of 'a
datatype 'a mylist = Empty | Cons of 'a * 'a mylist
datatype ('a, 'b) tree = 
	  Node of 'a * ('a, 'b) tree * ('a, 'b) tree
    | Leaf of 'b;
(* '' denotes equality types, which can be use with = operator *)
''a list * ''a -> bool

function pattern matching

fun f x = 
	case x of
		p1 => e1
	  | p2 => e2; 
(* is equivilant to *)
fun f p1 = e1
  | f p2 = e2

exceptions

exception MyException: exn
exception MyOtherException of int * int
 
(* raise exception *)
raise MyException
(* handle exception *)
e1 handle MyException => e2