Skip to content

Commit 7797b47

Browse files
committed
Add support for local bindings in do notation
1 parent 7f3fbe9 commit 7797b47

File tree

2 files changed

+46
-13
lines changed

2 files changed

+46
-13
lines changed

hackett-doc/scribblings/hackett/reference.scrbl

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1081,25 +1081,40 @@ different.
10811081

10821082
A flipped version of @racket[=<<].}
10831083

1084-
@defform[#:literals [<-]
1084+
@defform[#:literals [<- let letrec]
10851085
(do do-clause ... monadic-expr)
10861086
#:grammar
1087-
([do-clause [binding-id <- monadic-expr]
1088-
monadic-expr])]{
1087+
([do-clause monadic-do-clause
1088+
pure-do-clause]
1089+
[monadic-do-clause [binding-id <- monadic-expr]
1090+
monadic-expr]
1091+
[pure-do-clause (let binding-pair ...)
1092+
(letrec binding-pair ...)])]{
10891093

10901094
A convenient, imperative-style shorthand for a sequence of monadic expressions chained together with
10911095
@racket[>>=]. Each @racket[do-clause] corresponds to a single use of @racket[>>=], and each
10921096
@racket[monadic-expr] must have a type with the shape @racket[(_m _a)], where @racket[_m] is a
10931097
@racket[t:Monad].
10941098

1095-
Any use of @racket[do] with a single subform expands to the subform: @racket[(do _expr)] is equivalent
1096-
to @racket[_expr]. Each @racket[do-clause] introduces a use of @racket[>>=], with the result
1097-
potentially bound to a @racket[binding-id]. That is, @racket[(do [_x <- _m] _more ...+)] expands to
1098-
@racket[{_ma >>= (λ [_x] (do _more ...))}], and @racket[(do _m _more ...+)] expands to
1099-
@racket[{_ma >>= (λ [_] (do _more ...))}].
1100-
1101-
This is often much more readable than writing the uses of @racket[>>=] out by hand, especially when
1102-
the result of each action must be give a name.
1099+
The @racket[do] form is desugared using the following rules:
1100+
1101+
@itemlist[
1102+
#:style 'ordered
1103+
@item{Any use of @racket[do] with a single subform expands to the subform—@racket[(do _expr)] is
1104+
equivalent to @racket[_expr].}
1105+
@item{Each @racket[monadic-do-clause] introduces a use of @racket[>>=], with the result potentially
1106+
bound to a @racket[binding-id]. That is, @racket[(do [_x <- _m] _more ...+)] expands to
1107+
@racket[{_ma >>= (λ [_x] (do _more ...))}], and @racket[(do _m _more ...+)] expands to
1108+
@racket[{_ma >>= (λ [_] (do _more ...))}].}
1109+
@item{Each @racket[pure-do-clause] produces a local binding form @emph{without} any uses of
1110+
@racket[>>=], which is useful to create local bindings that are not monadic.
1111+
@racket[(do (let binding-pair ...) _more ...+)] expands to
1112+
@racket[(let (binding-pair ...) (do _more ...))], and
1113+
@racket[(do (letrec binding-pair ...) _more ...+)] expands to
1114+
@racket[(letrec (binding-pair ...) (do _more ...))].}]
1115+
1116+
Using @racket[do] is often much more readable than writing the uses of @racket[>>=] out by hand,
1117+
especially when it is helpful to give the result of each action a name.
11031118

11041119
@(hackett-examples
11051120
(do [xs <- (tail {1 :: 2 :: 3 :: 4 :: Nil})]
@@ -1109,7 +1124,21 @@ the result of each action must be give a name.
11091124
(do [xs <- (tail {1 :: 2 :: 3 :: Nil})]
11101125
[ys <- (tail xs)]
11111126
[zs <- (tail ys)]
1112-
(head zs)))}
1127+
(head zs))
1128+
(eval:alts
1129+
(do (let [x 1]
1130+
[y 2])
1131+
(println {"x is " ++ (show x)})
1132+
(println {"y is " ++ (show y)})
1133+
(let [z {x + y}])
1134+
(println {"x + y is " ++ (show z)}))
1135+
(unsafe-run-io!
1136+
(do (let [x 1]
1137+
[y 2])
1138+
(println {"x is " ++ (show x)})
1139+
(println {"y is " ++ (show y)})
1140+
(let [z {x + y}])
1141+
(println {"x + y is " ++ (show z)})))))}
11131142

11141143
@defthing[ap (t:forall [m a b] (t:Monad m) t:=> {(m {a t:-> b}) t:-> (m a) t:-> (m b)})]{
11151144

hackett-lib/hackett/private/prim/base.rkt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,15 +303,19 @@
303303
(flip =<<))
304304

305305
(define-syntax-parser do
306+
#:literals [let letrec]
306307
#:datum-literals [<-]
308+
[(_ {~and form ({~and form-id {~or let letrec}} ~! binding-pair ...)} rest ...+)
309+
(syntax/loc #'form
310+
(form-id (binding-pair ...) (do rest ...)))]
307311
[(_ {~and clause [~brackets ~! x:id <- e:expr]} rest ...+)
308312
(syntax/loc #'clause
309313
(>>= e (λ [x] (do rest ...))))]
310314
[(_ e:expr)
311315
#'e]
312316
[(_ e:expr rest ...+)
313317
(syntax/loc #'e
314-
(>>= e (λ [x] (do rest ...))))])
318+
(>>= e (λ [_] (do rest ...))))])
315319

316320
(defn ap : (forall [m a b] (Monad m) => {(m {a -> b}) -> (m a) -> (m b)})
317321
[[mf mx] (do [f <- mf]

0 commit comments

Comments
 (0)