| 65 | | == Non-recursive let and where bindings == |
| 66 | | |
| 67 | | First, we deal with ''non-recursive'' bindings only. |
| 68 | | In Haskell, let and where bindings can bind patterns. Their semantics is given by translation to a case, with an implicit implicit tilde (~). |
| 69 | | {{{ |
| 70 | | let [x,y] = e in b |
| | 68 | == Let and where bindings == |
| | 69 | |
| | 70 | In Haskell, let and where bindings can bind patterns. We propose to modify |
| | 71 | this by allowing an optional bang at the top level of the pattern. Thus for example: |
| | 72 | {{{ |
| | 73 | let ![x,y] = e in b |
| | 74 | }}} |
| | 75 | The "`!`" should not be regarded as part of the pattern; after all, |
| | 76 | in a function argument `![x,y]` means the same as `[x,y]`. Rather, the "`!`" |
| | 77 | is part of the syntax of `let` bindings. |
| | 78 | |
| | 79 | The semantics is simple; the above program is equivalent to: |
| | 80 | {{{ |
| | 81 | let p@[x,y] = e in p `seq` b |
| | 82 | }}} |
| | 83 | That is, for each bang pattern, invent a variable `p`, bind it to the |
| | 84 | banged pattern (removing the bang) with an as-pattern, and `seq` on it |
| | 85 | in the body of the `let`. (Thanks to Ben Rudiak-Gould for suggesting |
| | 86 | this idea.) |
| | 87 | |
| | 88 | A useful special case is when the pattern is a variable: |
| | 89 | Similarly |
| | 90 | {{{ |
| | 91 | let !y = f x in b |
| 74 | | case e of { ~[x,y] -> b } |
| 75 | | }}} |
| 76 | | In our proposal, if there is a bang right at the top of a let-bound pattern, |
| 77 | | then the implicit tilde is replaced with an explicit bang: |
| 78 | | {{{ |
| 79 | | let ![x,y] = e in b |
| 80 | | }}} |
| 81 | | means |
| 82 | | {{{ |
| 83 | | case e of { ![x,y] -> b } |
| 84 | | }}} |
| 85 | | which is the same as |
| 86 | | {{{ |
| 87 | | case e of { [x,y] -> b } |
| 88 | | }}} |
| 89 | | Similarly |
| 90 | | {{{ |
| 91 | | let !y = f x in b |
| 92 | | }}} |
| 93 | | means |
| 94 | | {{{ |
| 95 | | case f x of { !y -> b } |
| 96 | | }}} |
| 97 | | which evaluates the (f x), thereby giving a strict `let`. |
| 98 | | |
| 99 | | == Examples == |
| | 95 | let y = f x in y `seq` b |
| | 96 | }}} |
| | 97 | which evaluates the `(f x)`, thereby giving a strict `let`. |
| | 98 | |
| | 99 | A useful law is this. A ''non-recursive'' bang-pattern binding |
| | 100 | is equivalent to a `case` expression: |
| | 101 | {{{ |
| | 102 | let !p = <rhs> in <body> |
| | 103 | is equivalent to (when the let is non-recursive) |
| | 104 | case <rhs> of !p -> <body> |
| | 105 | }}} |
| | 106 | |
| | 107 | === Example === |
| 139 | | |
| 140 | | == Recursive let and where bindings == |
| 141 | | |
| 142 | | It is just about possible to make sense of bang-patterns in |
| 143 | | ''recursive'' let and where bindings. At first you might think they |
| 144 | | don't make sense: how can you evaluate it strictly if it doesn't exist |
| 145 | | yet? But consider |
| 146 | | {{{ |
| 147 | | let !xs = if funny then 1:xs else 2:xs in ... |
| 148 | | }}} |
| 149 | | Here the binding is recursive, but makes sense to think of it |
| 150 | | as equivalent to |
| 151 | | {{{ |
| 152 | | let xs = if funny then 1:xs else 2:xs |
| 153 | | in xs `seq` ... |
| 154 | | }}} |
| 155 | | But (a) it must be unusual, and (b) I have found it very hard to |
| 156 | | come up with a plausible translation (that deals with all the tricky |
| 157 | | points below). |
| 158 | | |
| 159 | | '''Conservative conclusion''': bang-pattern bindings must be non-recursive. |
| 185 | | == Tricky point: nested bangs == |
| | 186 | == Tricky point: nested bangs (part 1) == |
| | 187 | |
| | 188 | Consider this: |
| | 189 | {{{ |
| | 190 | let (x, Just !y) = <rhs> in <body> |
| | 191 | }}} |
| | 192 | Is `y` evaluted before `<body>` is begun? No, it isn't. That would be |
| | 193 | quite wrong. Pattern matching in a `let` is lazy; if any of the variables bound |
| | 194 | by the pattern is evaluated, then the whole pattern is matched. In this example, |
| | 195 | if `x` or `y` is evaluated, the whole pattern is matched, which in turn forces |
| | 196 | evaluation of `y`. The binding is equivalent to |
| | 197 | {{{ |
| | 198 | let t = <rhs> |
| | 199 | x = case t of { (x, Just !y) -> x } |
| | 200 | y = case t of { (x, Just !y) -> y } |
| | 201 | in <body> |
| | 202 | }}} |
| | 203 | |
| | 204 | == Tricky point: nested bangs (part 2) == |
| 214 | | t = case <rhs> of (x, Just !y) -> (x,y) |
| 215 | | x = fst t |
| 216 | | y = snd t |
| | 237 | t = <rhs> |
| | 238 | p = case t of p@(x, Just !y) -> p |
| | 239 | x = case t of p@(x, Just !y) -> x |
| | 240 | y = case t of p@(x, Just !y) -> y |
| | 241 | in |
| | 242 | p `seq` <body> |
| | 243 | }}} |
| | 244 | which is fine. |
| | 245 | |
| | 246 | You could also build an intermediate tuple, thus: |
| | 247 | {{{ |
| | 248 | let |
| | 249 | t = case <rhs> of p@(x, Just !y) -> (p,x,y) |
| | 250 | p = sel13 t |
| | 251 | x = sel23 t |
| | 252 | y = sel33 t |