[Haskell-cafe] Re: Applicative do?

oleg at okmij.org oleg at okmij.org
Sat Oct 10 05:36:44 EDT 2009

It seems the applicative do is almost identical to the form ML and
Scheme has had since the beginning. Perhaps that semantic similarity
might inform the syntactic debate.

> do a <- f
>    g
>    b <- h
>    pure $ foo a b
> into this:
> (\a b -> pure $ foo a b) <*> (f <*> g *> h)

This form, although very similar in appearance to the monadic do, is
very different in the scoping of bound identifiers. To me, that is
quite unfortunate, greatly increasing confusion. In the monadic do,
	x <- e1
	y <- e2
the identifier 'x' may appear in e2 and e3. To be more precise, the
scope of each bound identifier extends to the rest of the do-block and
can be used on the right-hand-side of other bindings in the rest of
the block. In the proposed applicative do above, the identifier 'a'
may not appear within g and h. Such a significant difference in
scoping calls for a different name at the very least; furthermore, it
calls for a compiler supports so that errors, such as using the
identifier 'a' in 'h' by mistake (common during editing and
refactoring) could be reported with an intelligent error message.

Also, in monadic-do, it makes sense to bind the same identifier
several times, as in do {x <- e1; x <- e2; e3}. It makes little sense
doing that in applicative-do, and probably should be disallowed.

Bob Atkey wrote:
>    applicatively
>    let x = foo
>    let y = bar
>    in <pure stuff>

But OCaml already has the right syntax -- and so does Scheme. That
syntax is let. In Scheme expression

	(let ((x e1)
	      (y e2)
	      (_ e11))

only the expression e3 is in scope of x and y. Emphatically, e2 is not
in scope in x; so the value bound to x cannot be used during the
evaluation of e2. The expression e11 is evaluated only for its
side-effect (underscore is just an identifier, I could've used
`dummy'). Again, the expression e11 is not in scope of x and y. All
identifiers bound by the let-clause must be distinct. 

The equivalent OCaml construction is

      let x = e1 and y = e2 and _ = e11 in e3

If, by mistake, we do mention x or y in e11 (and there are no global
binding to x and y), OCaml will report the error about the unbound

It seems the parallel let of Scheme or OCaml fall short of Applicative in
one respect: the order of evaluating binding is not specified. If we
agreed that the binders of a parallel let must be evaluated in order,
we would get exactly the desired `applicative-do'. Perhaps, a better
form in OCaml would be

	let seq x = foo and y = bar in e3
	let x = foo andalso y = bar in e3
Perhaps it may make sense to use the keyword `let' rather than 'do' in
Haskell as well?

P.S. For completeness, both Scheme and OCaml have the exact equivalent
of monadic do. Haskell's
	x <- e1
	y <- e2
        x <- e3
can be written in Scheme as
	(let* ((x e1) (y e2) (x e3) (_ e4)) e5)
and in OCaml as
	let x = e1 in 
	let y = e2 in 
	let x = e3 in 
	let () = e4 in 
The latter pattern is very common in OCaml.

More information about the Haskell-Cafe mailing list