type namedInt = (string * int); val xBinding:namedInt = ("x", 15);
Type synonyms are simple shorthand for whatever's on the right hand side of the type synonym declaration. More interesting are user-defined data types:
(* Simple "enumeration" *) datatype Colors = Red | Orange | Yellow | Green | Blue | Indigo | Violet; (* A type with components *) datatype MyNumber = MyInt of int | MyReal of real; (* A recursive type *) datatype StringTree = Nothing | StringTreeNode of string * StringTree * StringTree; (* A recursive polymorphic type *) datatype 'a Tree = NullNode | TreeNode of 'a * 'a Tree * 'a Tree; (* Instantiating a tree node *) val oneTree = TreeNode(4, TreeNode(2, NullNode, NullNode), TreeNode(7, NullNode, NullNode));
ML has a very powerful, flexible, and expressive module system. We won't have time to cover them fully this quarter, but here's the most basic use of modules:
structure MyStuff = struct datatype MyNum = MyInt of int | MyReal of real; val myZero = MyInt(0); fun myAdd(MyInt(x), MyInt(y)) = MyInt(x+y) | myAdd(MyInt(x), MyReal(y)) = MyReal(real(x) + y) | myAdd(MyReal(x), MyInt(y)) = MyReal(x + real(y)) | myAdd(MyReal(x), MyReal(y)) = MyReal(x + y); end;
The above creates a struct (completely unlike a C struct in form and purpose!), and binds it to a structure name. Now, if any code outside this block wants to access any of the functions or types therein, that code must prefix the structure name:
val seal = MyStuff.MyReal(3.14159); val bear = MyStuff.MyInt(21); val theFreakishSealBear = MyStuff.myAdd(seal, bear);