Programming in Standard ML – Part 2
Chapter 4 – Functions
Lambda expressions are written as:
fn var : typ => exp
For example:
fn x : real => Math.sqrt (Math.sqrt x) (fn x : real => Math.sqrt (Math.sqrt x)) (16.0) val fourthroot : real -> real = fn x : real => Math.sqrt (Math.sqrt x) fourthroot 16.0
ML provides a special syntax for function bindings that’s more concise:
fun fourthroot (x:real):real = Math.sqrt (Math.sqrt x)
By experimenting (I expect this is covered later), I found the following is sufficient due to type inference:
fun fourthroot x = Math.sqrt (Math.sqrt x)
Local val bindings may shadow parameters and other val bindings. For example, in the following, the last occurrence of x refers to the parameter of h, but the preceding two occurrences of x refer to the local binding with a value of 2.0:
fun h(x:real):real = let val x:real = 2.0 in x+x end * x
Chapter 5 – Products and Records
The n-tuple is the simplest form of aggregate data structure. They are of the form:
(val1, … ,valn)
An n-tuple is a value of a product type of the form:
typ1* … *typn
For example:
val pair : int * int = (2, 3) val triple : int * real * string = (2, 2.0, "2") val pair_of_pairs : (int * int) * (real * real) = ((2,3),(2.0,3.0))
A 0-tuple, also known as a null tuple, is the empty sequence of values, ( ). It is a value of type unit. 1-tuples are absent from the language.
Tuple Patterns
We can use pattern matching to extract portions of an n-tuple. For example, in the code snippet below, r will be equal to 3.14. Underscores indicate “don’t care” positions:
val foo : (int * string) * (real * char) = ((7,"hello"),(3.14,#"z")) val ((_, _), (r:real, _)) = foo
We can give names to the first and second components of the pair – using the REPL:
- val (is:int*string,rc:real*char) = foo; val is = (7,"hello") : int * string val rc = (3.14,#"z") : real * char
A pattern is one of three forms:
- A variable pattern of the form var : typ
- A tuple pattern of the form (pat1, …, patn), where each pati is a pattern. This includes as a special case the null-tuple pattern, ()
- A wildcard pattern of the form _
Record Types
Tuples can become more difficult to use as the number of elements increases. Record types allow labeling each component. A record type has the form:
{ lab1:typ1, …, labn:typn}
A record value has the form:
{ lab1=val1, …, labn=valn}
A record pattern has the form:
{ lab1=pat1, …, labn=patn}
For example, the record type hyperlink is defined as follows:
type hyperlink = { protocol : string, address : string, display : string }
The following record binding defines a variable of type hyperlink:
val mailto : hyperlink = { protocol = "mailto", address = "foo@bar.com", display = "Brian Adkins" }
The following record binding:
val { protocol=prot, display=disp, address=addr } = mailto
decomposes into the three variable bindings:
val prot = "mailto" val addr = "foo@bar.com" val disp = "Brian Adkins"
We can use wildcard to extract selected fields:
val {protocol=prot, address=_, display=_ } = mailto
However, this isn’t very helpful with many fields, so we can use ellipsis patterns:
val {protocol=prot, ... } = mailto
ML provides an abbreviated form of record pattern {lab1,…labn} which stands for { lab1=var1, …, labn=varn} where the variables have the same name as the corresponding labels. For example, the following:
val { protocol, address, display } = mailto
decomposes into these bindings:
val protocol = "mailto" val address = "foo@bar.com" val display = "Brian Adkins"
Multiple Arguments and Multiple Results
fun dist (x:real, y:real):real = sqrt (x*x + y*y)
Keyword parameters are supported through record patterns:
fun dist’ {x=x:real, y=y:real} = sqrt (x*x + y*y)
Invoked as follows:
dist' {x=2.0,y=3.0}
Functions with multiple results may be thought of as functions yield tuples (or records).
fun dist2 (x:real, y:real):real*real = (sqrt (x*x+y*y), abs(x-y))
Sharp notation allows us to conveniently access the Nth item in a tuple.
- val foo = (3,7,5,2); val foo = (3,7,5,2) : int * int * int * int - #3 foo; val it = 5 : int
A similar notation is used for record field selection:
- val foo = { name = "Brian", phone = "555-1212" }; val foo = {name="Brian",phone="555-1212"} : {name:string, phone:string} - #name foo; val it = "Brian" : string
However, Harper states, “Use of the sharp notation is strongly discouraged!“
Leave a Reply