(*
The truth about lists
We've seen how we can create user-defined datatypes, build them with
constructors, and decompose them with pattern matching. This gives
us enough power to easily write our own list datatype (and any other
data structure imaginable)
*)
datatype intlist = Cons of int * intlist | EmptyList;
fun sum_intlist l =
case l of
EmptyList => 0
| Cons(x,rest) => x + sum_intlist rest
val some_numbers = Cons(1, Cons(2, Cons(3, Cons(4, EmptyList))))
val sum = sum_intlist some_numbers
fun intlist_head l =
case l of
(* Exceptions will be covered in more detail at a later time *)
EmptyList => raise Empty
| Cons(x,rest) => x
fun intlist_tail l =
case l of
EmptyList => raise Empty
| Cons(x,rest) => rest
val head = intlist_head some_numbers
val tail = intlist_tail some_numbers
(*
In reality, the "built in" ML list is nothing more than a datatype
with funny syntax. The empty list is written [] and Cons is written
as ::, but between the arguments as an infix operator.
*)
fun sum_list l =
case l of
[] => 0
| x::rest => x + sum_list rest
val some_more_numbers = [5, 6, 7, 8]
val another_sum = sum_list some_more_numbers
(*
Options are also another simple datatype, but with normal syntax.
We could define our own option type easily
*)
datatype intoption = Nothing | Something of int
fun add_intoptions (Nothing, y) = y
| add_intoptions (x, Nothing) = x
| add_intoptions (Something a, Something b) = Something (a + b)
val five = add_intoptions (Something (5), Nothing)
val four = add_intoptions (Something (2), Something (2))
(*
Pattern matching the provided option type is also possible
(and prefered over defining your own type)
*)
fun add_intoptions2 (NONE, y) = y
| add_intoptions2 (x, NONE) = x
| add_intoptions2 (SOME a, SOME b) = SOME (a + b)
(*
Records
As you've seen, records are essentially tuples with named fields,
although it is more correct to say that tuples are simply records with
fields named 1, 2, 3, ...
*)
fun add (pair :int * int) =
#1 pair + #2 pair
val x1 = add (3, 6)
val x2 = add {1=3, 2=6}
val x3 = add {2=6, 1=3}
(* Records can be pattern matched just like tuples *)
datatype colororgrey = Color of {red :real, green :real, blue :real} |
Grey of {shade :real}
fun togrey c =
case c of
Grey g => Grey g
| Color {red=r, green=g, blue=b} => Grey {shade=(r + g + b) / 3.0}