|
|
1.1 root 1: A set of new primitives has been added to ML to give access to continuations:
2:
3: type 'a cont
4: val callcc : ('a cont -> 'a) -> 'a
5: val throw : 'a cont -> 'a -> 'b
6:
7: The continuation of an expression is an abstraction of what the system
8: will do with the value of the expression. For example in the
9: expression:
10:
11: if a orelse b then foo() else goo()
12:
13: the continuation of the expression "a orelse b" can be described in
14: words as "if the value is true then compute foo() otherwise compute
15: goo()" and then continue in the context of the if-expression. Usually
16: the continuation of an expression is implicit, however, the primitive
17: callcc allows the programmer to capture and use these continuations.
18:
19: The primitive callcc takes a function as an argument and applies it to
20: the current continuation. The continuation of an expression of type 'a
21: has type 'a cont and is a first-class object. To capture the
22: continuation described above one would write:
23:
24: if callcc(fn k => a orelse b) then foo() else goo
25:
26: Here the continuation of the callcc-application is captured by being
27: bound to k, but it is not used. Because the continuation is not used
28: the result is simply the result of the expression "a orelse b". To
29: use the continuation a value must be supplied, and the computation
30: continues as if that value where the result of the callcc-application.
31: This is called throwing the continuation a value; it is performed by
32: applying "throw" to the continuation and the value.
33:
34: if callcc(fn k => (throw k false) orelse b) then foo() else goo()
35:
36: Here, when the continuation k is thrown the value false, "orelse b" is simply
37: ignored, the callcc-application returns false, and goo() is then evaluated.
38:
39: The type returned by a throw-expression is unconstrained like that of
40: a raise-expression and for the same reason: neither of these
41: expressions ever return.
42:
43: One of the less interesting uses of callcc is as an alternative to
44: exception handlers. For example:
45:
46: exception Prod
47:
48: fun prod l = let fun loop [] = 1
49: | loop(0::r) = raise Prod
50: | loop(a::r) = a * loop r
51: in loop l handle Prod => 0
52: end
53:
54: can be written with callcc as follows:
55:
56: fun prod l = callcc(fn exit =>
57: let fun loop [] = 1
58: | loop(0::r) = throw exit 0
59: | loop(a::r) = a * loop r
60: in loop l
61: end)
62:
63: But continuations are more general than exception handlers and can be
64: used to implement sophisticated control structures. These more complex
65: uses often involve subtle interactions with the type system. For just
66: a taste of the techniques involved consider the following example
67: implementing an infinite loop.
68:
69: datatype State = S of State cont
70: fun state_throw (S k) = throw k
71:
72: let val s = callcc S
73: in state_throw s s
74: end
75:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.