Import Upstream version 20180207
[hcoop/debian/mlton.git] / doc / guide / src / GenerativeException.adoc
CommitLineData
7f918cf1
CE
1GenerativeException
2===================
3
4In <:StandardML:Standard ML>, exception declarations are said to be
5_generative_, because each time an exception declaration is evaluated,
6it yields a new exception.
7
8The following program demonstrates the generativity of exceptions.
9
10[source,sml]
11----
12exception E
13val e1 = E
14fun isE1 (e: exn): bool =
15 case e of
16 E => true
17 | _ => false
18exception E
19val e2 = E
20fun isE2 (e: exn): bool =
21 case e of
22 E => true
23 | _ => false
24fun pb (b: bool): unit =
25 print (concat [Bool.toString b, "\n"])
26val () = (pb (isE1 e1)
27 ;pb (isE1 e2)
28 ; pb (isE2 e1)
29 ; pb (isE2 e2))
30----
31
32In the above program, two different exception declarations declare an
33exception `E` and a corresponding function that returns `true` only on
34that exception. Although declared by syntactically identical
35exception declarations, `e1` and `e2` are different exceptions. The
36program, when run, prints `true`, `false`, `false`, `true`.
37
38A slight modification of the above program shows that even a single
39exception declaration yields a new exception each time it is
40evaluated.
41
42[source,sml]
43----
44fun f (): exn * (exn -> bool) =
45 let
46 exception E
47 in
48 (E, fn E => true | _ => false)
49 end
50val (e1, isE1) = f ()
51val (e2, isE2) = f ()
52fun pb (b: bool): unit =
53 print (concat [Bool.toString b, "\n"])
54val () = (pb (isE1 e1)
55 ; pb (isE1 e2)
56 ; pb (isE2 e1)
57 ; pb (isE2 e2))
58----
59
60Each call to `f` yields a new exception and a function that returns
61`true` only on that exception. The program, when run, prints `true`,
62`false`, `false`, `true`.
63
64
65== Type Safety ==
66
67Exception generativity is required for type safety. Consider the
68following valid SML program.
69
70[source,sml]
71----
72fun f (): ('a -> exn) * (exn -> 'a) =
73 let
74 exception E of 'a
75 in
76 (E, fn E x => x | _ => raise Fail "f")
77 end
78fun cast (a: 'a): 'b =
79 let
80 val (make: 'a -> exn, _) = f ()
81 val (_, get: exn -> 'b) = f ()
82 in
83 get (make a)
84 end
85val _ = ((cast 13): int -> int) 14
86----
87
88If exceptions weren't generative, then each call `f ()` would yield
89the same exception constructor `E`. Then, our `cast` function could
90use `make: 'a -> exn` to convert any value into an exception and then
91`get: exn -> 'b` to convert that exception to a value of arbitrary
92type. If `cast` worked, then we could cast an integer as a function
93and apply. Of course, because of generative exceptions, this program
94raises `Fail "f"`.
95
96
97== Applications ==
98
99The `exn` type is effectively a <:UniversalType:universal type>.
100
101
102== Also see ==
103
104 * <:GenerativeDatatype:>