Commit | Line | Data |
---|---|---|
7f918cf1 CE |
1 | Lazy |
2 | ==== | |
3 | ||
4 | In a lazy (or non-strict) language, the arguments to a function are | |
5 | not evaluated before calling the function. Instead, the arguments are | |
6 | suspended and only evaluated by the function if needed. | |
7 | ||
8 | <:StandardML:Standard ML> is an eager (or strict) language, not a lazy | |
9 | language. However, it is easy to delay evaluation of an expression in | |
10 | SML by creating a _thunk_, which is a nullary function. In SML, a | |
11 | thunk is written `fn () => e`. Another essential feature of laziness | |
12 | is _memoization_, meaning that once a suspended argument is evaluated, | |
13 | subsequent references look up the value. We can express this in SML | |
14 | with a function that maps a thunk to a memoized thunk. | |
15 | ||
16 | [source,sml] | |
17 | ---- | |
18 | signature LAZY = | |
19 | sig | |
20 | val lazy: (unit -> 'a) -> unit -> 'a | |
21 | end | |
22 | ---- | |
23 | ||
24 | This is easy to implement in SML. | |
25 | ||
26 | [source,sml] | |
27 | ---- | |
28 | structure Lazy: LAZY = | |
29 | struct | |
30 | fun lazy (th: unit -> 'a): unit -> 'a = | |
31 | let | |
32 | datatype 'a lazy_result = Unevaluated of (unit -> 'a) | |
33 | | Evaluated of 'a | |
34 | | Failed of exn | |
35 | ||
36 | val r = ref (Unevaluated th) | |
37 | in | |
38 | fn () => | |
39 | case !r of | |
40 | Unevaluated th => let | |
41 | val a = th () | |
42 | handle x => (r := Failed x; raise x) | |
43 | val () = r := Evaluated a | |
44 | in | |
45 | a | |
46 | end | |
47 | | Evaluated a => a | |
48 | | Failed x => raise x | |
49 | end | |
50 | end | |
51 | ---- |