Define a tree datatype, tree, where a leaf has an integer label and an interior node has an
integer label and two child trees. For example,
- node (5, leaf 6, leaf 7);
val it = node (5,leaf 6,leaf 7) : tree
This definition should be one line of code.
ML has higher order functions
- fun h f x = f (x+1) * 2 ;
val h = fn : (int -> int) -> int -> int
- fun h f x = f x * 2 ; (* h is polymorphic *)
val h = fn : ('a -> int) -> 'a -> int
Defining map. Note that it is polymorphic
- fun map f [] = []
= | map f (z::zs) = f z :: map f zs ;
val map = fn : ('a -> 'b) -> 'a list -> 'b list
- (* What is the type of the compose function? *)
- fun compose f g x = f (g x) ;
val compose = fn : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b
LAMBDA expressions are written: fn =>
- fn x => x + 1 ;
val it = fn : int -> int
- (fn x => x + 1) 7 ;
val it = 8 : int
- fun f x = fn y => x + y ;
val f = fn : int -> int -> int
- f 5 7 ;
val it = 12 : int
- (* tuples can be used in patterns *)
- val x = (3, "hello") ;
val x = (3,"hello") : int * string
- fun foo (y,z) = if y = 3 then z else "no" ;
val foo = fn : int * string -> string
- foo x ;
val it = "hello" : string
- [] ; (* Not a function, but is polymorphic! *)
val it = [] : 'a list
- fun f x = x ; (* The identity function is the only terminating function
of type 'a -> 'a *)
val f = fn : 'a -> 'a
- fun g x = if true then g x else x ; (* Here's a non-terminating function of type 'a -> 'a *)
val g = fn : 'a -> 'a
Sometimes, you need to declare the types of parameters. For example, you might need to specify the parameter is a real rather than an int
- fun f (x:real) y = x + y ;
val f = fn : real -> real -> real
Patterns can be used in variable definitions, allowing for simultanenous variable definitions
- val (x,y) = (4, 3.5) ;
val x = 4 : int
val y = 3.5 : real
- val (x::xs) = [1,2,3,4] ;
val x = 1 : int
val xs = [2,3,4] : int list
Defining nested variables and functions using a let
- fun g x =
= let val y = x * 2
= fun h z = z * y
= in h x
= end ;
val g = fn : int -> int
For mutually recursive functions, use “and”
- fun f x = if x = 0 then 1 else x * g (x-1)
= and
= g 0 = 1
= | g n = n * f (n-1)
= ;
val f = fn : int -> int
val g = fn : int -> int
The boolean operators are “andalso” and “orelse”
- true andalso false ;
val it = false : bool
- true orelse false ;
val it = true : bool
Defining a new type using “datatype”, enumerating all the elements of the new type
- datatype stoplight = red | green | yellow ;
datatype stoplight = green | red | yellow
- red ;
val it = red : stoplight
These values can be used as patterns
- fun drive red = "stop"
= | drive green = "go"
= | drive yellow = "go faster" ;
val drive = fn : stoplight -> string
Associating values with each alternative in a datatype declaration
- datatype vehicle = car of int | truck of bool | boat of int list ;
datatype vehicle = boat of int list | car of int | truck of bool
- (* car needs to take an integer value as a parameter *)
- car 6 ;
val it = car 6 : vehicle
- truck true; (* truck needs a boolean value *)
val it = truck true : vehicle
- boat [1,2,3]; (* boat needs an int list *)
val it = boat [1,2,3] : vehicle
Can also use these in patterns to define functions
- fun silly (car x) = x*2
= | silly (truck y) = if y then 3 else 4
= | silly (boat z) = length z ;
val silly = fn : vehicle -> int
- silly (car 7) ;
val it = 14 : int
- silly (boat [1,2,3,4]) ;
val it = 4 : int
Datatypes can be recursive! That is, the type you’re defining can appear on the right hand side of the definition
- datatype tree = leaf of int | node of tree * tree ;
datatype tree = leaf of int | node of tree * tree
Constructing a tree
- val mytree = node (node (leaf 3, leaf 4), leaf 5) ;
val mytree = node (node (leaf 3,leaf 4),leaf 5) : tree
Fringe returns a list of the labels associated with the leaves of a tree
- fun fringe (leaf x) = [x]
= | fringe (node (left,right)) = fringe left @ fringe right ;
val fringe = fn : tree -> int list
- fringe mytree ;
val it = [3,4,5] : int list
Datatypes can be polymorphic
- datatype 'a tree = leaf of 'a | node of 'a tree * 'a tree ;
datatype 'a tree = leaf of 'a | node of 'a tree * 'a tree
- leaf 5 ;
val it = leaf 5 : int tree
- leaf ["hello"] ;
val it = leaf ["hello"] : string list tree
Use the same definition of fringe as above. Now it’s polymorphic!
- fun fringe (leaf x) = [x]
= | fringe (node (left,right)) = fringe left @ fringe right ;
val fringe = fn : 'a tree -> 'a list
Using infix operators as functions
- (op +)(3,4) ;
val it = 7 : int
- fun f g = g(3,4) ;
val f = fn : (int * int -> 'a) -> 'a
- f (op +) ;
val it = 7 : int