summaryrefslogtreecommitdiffhomepage
path: root/standard-ml.html.markdown
diff options
context:
space:
mode:
Diffstat (limited to 'standard-ml.html.markdown')
-rw-r--r--standard-ml.html.markdown66
1 files changed, 58 insertions, 8 deletions
diff --git a/standard-ml.html.markdown b/standard-ml.html.markdown
index b545f3e1..143980e7 100644
--- a/standard-ml.html.markdown
+++ b/standard-ml.html.markdown
@@ -3,13 +3,15 @@ language: "Standard ML"
contributors:
- ["Simon Shine", "http://shine.eu.org/"]
- ["David Pedersen", "http://lonelyproton.com/"]
+ - ["James Baker", "http://www.jbaker.io/"]
+ - ["Leo Zovic", "http://langnostic.inaimathi.ca/"]
---
Standard ML is a functional programming language with type inference and some
side-effects. Some of the hard parts of learning Standard ML are: Recursion,
pattern matching, type inference (guessing the right types but never allowing
-implicit type conversion). If you have an imperative background, not being able
-to update variables can feel severely inhibiting.
+implicit type conversion). Standard ML is distinguished from Haskell by including
+references, allowing variables to be updated.
```ocaml
(* Comments in Standard ML begin with (* and end with *). Comments can be
@@ -135,9 +137,29 @@ val mixup = [ ("Alice", 39),
val good_bad_stuff =
(["ice cream", "hot dogs", "chocolate"],
- ["liver", "paying the rent" ]) (* string list * string list *)
+ ["liver", "paying the rent" ]) (* : string list * string list *)
+(* Records are tuples with named slots *)
+
+val rgb = { r=0.23, g=0.56, b=0.91 } (* : {b:real, g:real, r:real} *)
+
+(* You don't need to declare their slots ahead of time. Records with
+ different slot names are considered different types, even if their
+ slot value types match up. For instance... *)
+
+val Hsl = { H=310.3, s=0.51, l=0.23 } (* : {H:real, l:real, s:real} *)
+val Hsv = { H=310.3, s=0.51, v=0.23 } (* : {H:real, s:real, v:real} *)
+
+(* ...trying to evaluate `Hsv = Hsl` or `rgb = Hsl` would give a type
+ error. While they're all three-slot records composed only of `real`s,
+ they each have different names for at least some slots. *)
+
+(* You can use hash notation to get values out of tuples. *)
+
+val H = #H Hsv (* : real *)
+val s = #s Hsl (* : real *)
+
(* Functions! *)
fun add_them (a, b) = a + b (* A simple function that adds two numbers *)
val test_it = add_them (3, 4) (* gives 7 *)
@@ -224,17 +246,26 @@ fun fibonacci 0 = 0 (* Base case *)
| fibonacci 1 = 1 (* Base case *)
| fibonacci n = fibonacci (n - 1) + fibonacci (n - 2) (* Recursive case *)
-(* Pattern matching is also possible on composite types like tuples and lists.
- Writing "fun solve2 (a, b, c) = ..." is in fact a pattern match on the one
- three-tuple solve2 takes as argument. Similarly, but less intuitively, you
- can match on a list consisting of elements in it (from the beginning of the
- list only). *)
+(* Pattern matching is also possible on composite types like tuples, lists and
+ records. Writing "fun solve2 (a, b, c) = ..." is in fact a pattern match on
+ the one three-tuple solve2 takes as argument. Similarly, but less intuitively,
+ you can match on a list consisting of elements in it (from the beginning of
+ the list only). *)
fun first_elem (x::xs) = x
fun second_elem (x::y::xs) = y
fun evenly_positioned_elems (odd::even::xs) = even::evenly_positioned_elems xs
| evenly_positioned_elems [odd] = [] (* Base case: throw away *)
| evenly_positioned_elems [] = [] (* Base case *)
+(* When matching on records, you must use their slot names, and you must bind
+ every slot in a record. The order of the slots doesn't matter though. *)
+
+fun rgbToTup {r, g, b} = (r, g, b) (* fn : {b:'a, g:'b, r:'c} -> 'c * 'b * 'a *)
+fun mixRgbToTup {g, b, r} = (r, g, b) (* fn : {b:'a, g:'b, r:'c} -> 'c * 'b * 'a *)
+
+(* If called with {r=0.1, g=0.2, b=0.3}, either of the above functions
+ would return (0.1, 0.2, 0.3). But it would be a type error to call them
+ with {r=0.1, g=0.2, b=0.3, a=0.4} *)
(* Higher order functions: Functions can take other functions as arguments.
Functions are just other kinds of values, and functions don't need names
@@ -383,6 +414,25 @@ val test_poem = readPoem "roses.txt" (* gives [ "Roses are red,",
"Violets are blue.",
"I have a gun.",
"Get in the van." ] *)
+
+(* We can create references to data which can be updated *)
+val counter = ref 0 (* Produce a reference with the ref function *)
+
+(* Assign to a reference with the assignment operator *)
+fun set_five reference = reference := 5
+
+(* Read a reference with the dereference operator *)
+fun equals_five reference = !reference = 5
+
+(* We can use while loops for when recursion is messy *)
+fun decrement_to_zero r = if !r < 0
+ then r := 0
+ else while !r >= 0 do r := !r - 1
+
+(* This returns the unit value (in practical terms, nothing, a 0-tuple) *)
+
+(* To allow returning a value, we can use the semicolon to sequence evaluations *)
+fun decrement_ret x y = (x := !x - 1; y)
```
## Further learning