silo:std.option

Source src/silo:std.option

1##! Option combinator methods — the `Opt` trait.
2##!
3##! Gives `Option` values the familiar `.map`, `.and-then`,
4##! `.unwrap-or`, `.ok-or` surface.
5
6:use
7  :open core Bool Option None Some Pair Result Ok Err Str
8  :open traits Eq
9  :open effects.panic panic
10:end
11
12# si[impl stdlib.option.trait+1]
13## Combinator surface on `(Option a)`. Every method dispatches on the None/Some tag
14## and delegates to a user-provided quotation for the Some case. Short-circuiting
15## variants (`.and-then`, `.or-else`, `.unwrap-or-else`) only evaluate their quotation
16## when the Option's tag selects that branch.
17:trait(pub) Opt
18  ## True iff the Option carries a value.
19  .is-some ( (Option a) -> Bool )
20  ## True iff the Option is `None`.
21  .is-none ( (Option a) -> Bool )
22  ## Apply a function to the contained value, preserving the Some/None structure.
23  .map ( [a -> b] (Option a) -> (Option b) )
24  ## Chain into another Option-producing computation (the monadic bind).
25  .and-then ( [a -> (Option b)] (Option a) -> (Option b) )
26  ## Supply a replacement Option when the receiver is `None`.
27  .or-else ( [-> (Option a)] (Option a) -> (Option a) )
28  ## Return the contained value, panicking if `None`.
29  .unwrap ( (Option a) -> a ) +Panic
30  ## Return the contained value, panicking with the supplied message if `None`.
31  .expect ( (Option a) Str -> a ) +Panic
32  ## Return the contained value, or a fallback if `None`.
33  .unwrap-or ( a (Option a) -> a )
34  ## Return the contained value, or invoke a thunk to produce the fallback if `None`.
35  .unwrap-or-else ( [-> a] (Option a) -> a )
36  ## Keep the value iff it satisfies a predicate; otherwise become `None`.
37  .filter ( [a -> Bool] (Option a) -> (Option a) )
38  ## Collapse `(Option (Option a))` into `(Option a)` by one layer.
39  .flatten ( (Option (Option a)) -> (Option a) )
40  ## Convert to a Result, supplying a fixed error value for the `None` case.
41  .ok-or ( err (Option a) -> (Result a err) )
42  ## Convert to a Result, invoking a thunk to produce the error for the `None` case.
43  .ok-or-else ( (Option a) [ -> err] -> (Result a err) )
44  .map-or ( (Option a) b [a -> b] -> b )
45  .map-or-else ( (Option a) [ -> b] [a -> b] -> b )
46  .and ( (Option a) (Option b) -> (Option b) )
47  .take ( (Option a) -> (Option a) (Option a) )
48  .is-some-and ( (Option a) [a -> Bool] -> Bool )
49  .is-none-or ( (Option a) [a -> Bool] -> Bool )
50  .xor ( (Option a) (Option a) -> (Option a) )
51  .zip ( (Option a) (Option b) -> (Option (Pair a b)) )
52  .transpose ( (Option (Result a e)) -> (Result (Option a) e) )
53  .unzip ( (Option (Pair a b)) -> (Option a) (Option b) )
54  .zip-with ( (Option a) (Option b) [a b -> c] -> (Option c) )
55  .take-if ( (Option a) [a -> Bool] -> (Option a) (Option a) )
56  ## Call a quotation on the inner value for side effects, returning the Option unchanged.
57  .inspect ( (Option a) [a -> ] -> (Option a) ) +eff
58  ## True iff the Option is `Some` and the inner value equals the supplied argument.
59  .contains ( (Option a) a -> Bool ) { (Eq a a) }
60:end
61
62:impl Opt (Option elem)
63  .is-some ( (Option a) -> Bool )
64    :match | None => false | Some _ => true :end ;
65
66  .is-none ( (Option a) -> Bool )
67    :match | None => true | Some _ => false :end ;
68
69  .map ( [a -> b] (Option a) -> (Option b) )
70    :match
71      | None   => drop None
72      | Some x => x swap .call Some
73    :end ;
74
75  .and-then ( [a -> (Option b)] (Option a) -> (Option b) )
76    :match
77      | None   => drop None
78      | Some x => x swap .call
79    :end ;
80
81  .or-else ( [-> (Option a)] (Option a) -> (Option a) )
82    :match
83      | None   => .call
84      | Some x => drop x Some
85    :end ;
86
87  # si[impl stdlib.option.unwrap+1]
88  .unwrap ( (Option a) -> a ) +Panic
89    :match
90      | None   => "unwrap called on None" panic
91      | Some x => x
92    :end ;
93
94  # si[impl stdlib.option.expect]
95  .expect ( (Option a) Str -> a ) +Panic
96    pop-> msg
97    :match
98      | None   => msg panic
99      | Some x => x
100    :end ;
101
102  .unwrap-or ( a (Option a) -> a )
103    :match
104      | None   =>
105      | Some x => drop x
106    :end ;
107
108  .unwrap-or-else ( [-> a] (Option a) -> a )
109    :match
110      | None   => .call
111      | Some x => drop x
112    :end ;
113
114  .filter ( [a -> Bool] (Option a) -> (Option a) )
115    :match
116      | None   => drop None
117      | Some x => x swap .call :if x Some :else None :end
118    :end ;
119
120  .flatten ( (Option (Option a)) -> (Option a) )
121    :match
122      | None       => None
123      | Some inner => inner
124    :end ;
125
126  .ok-or ( err (Option a) -> (Result a err) )
127    :match
128      | None   => Err
129      | Some x => drop x Ok
130    :end ;
131
132  .ok-or-else ( (Option a) [ -> err] -> (Result a err) )
133    pop-> f
134    :match
135      | None   => f .call Err
136      | Some x => x Ok
137    :end ;
138
139  .map-or ( (Option a) b [a -> b] -> b )
140    pop-> f pop-> default
141    :match
142      | None   => default
143      | Some x => x f .call
144    :end ;
145
146  .map-or-else ( (Option a) [ -> b] [a -> b] -> b )
147    pop-> f pop-> default
148    :match
149      | None   => default .call
150      | Some x => x f .call
151    :end ;
152
153  .and ( (Option a) (Option b) -> (Option b) )
154    swap :match
155      | None   => drop None
156      | Some _ =>
157    :end ;
158
159  .take ( (Option a) -> (Option a) (Option a) )
160    None ;
161
162  .is-some-and ( (Option a) [a -> Bool] -> Bool )
163    pop-> pred
164    :match
165      | None   => false
166      | Some x => x pred .call
167    :end ;
168
169  .is-none-or ( (Option a) [a -> Bool] -> Bool )
170    pop-> pred
171    :match
172      | None   => true
173      | Some x => x pred .call
174    :end ;
175
176  .xor ( (Option a) (Option a) -> (Option a) )
177    swap :match
178      | None   =>
179      | Some a => :match
180          | None   => a Some
181          | Some _ => None
182        :end
183    :end ;
184
185  .zip ( (Option a) (Option b) -> (Option (Pair a b)) )
186    swap :match
187      | None   => drop None
188      | Some a => :match
189          | None   => None
190          | Some b => a b Pair Some
191        :end
192    :end ;
193
194  .transpose ( (Option (Result a e)) -> (Result (Option a) e) )
195    :match
196      | None        => None Ok
197      | Some inner  => inner :match
198          | Ok x  => x Some Ok
199          | Err e => e Err
200        :end
201    :end ;
202
203  .unzip ( (Option (Pair a b)) -> (Option a) (Option b) )
204    :match
205      | None   => None None
206      | Some p => p .a Some p .b Some
207    :end ;
208
209  .zip-with ( (Option a) (Option b) [a b -> c] -> (Option c) )
210    pop-> f
211    swap :match
212      | None   => drop None
213      | Some a => :match
214          | None   => None
215          | Some b => a b f .call Some
216        :end
217    :end ;
218
219  .take-if ( (Option a) [a -> Bool] -> (Option a) (Option a) )
220    pop-> pred
221    peek-> original
222    :match
223      | None   => original original
224      | Some x => x pred .call :if original None :else original original :end
225    :end ;
226
227  # si[impl stdlib.option.inspect]
228  .inspect ( (Option a) [a -> ] -> (Option a) ) +eff
229    pop-> f
230    :match
231      | None   => None
232      | Some x => x f .call x Some
233    :end ;
234
235  # si[impl stdlib.option.contains]
236  .contains ( (Option a) a -> Bool ) { (Eq a a) }
237    pop-> val
238    :match
239      | None   => false
240      | Some x => x val .=
241    :end ;
242:end