Consider this example:
{-# language ApplicativeDo #-}
module X where
data Tuple a b = Tuple a b deriving Show
instance Functor (Tuple a) where
fmap f (Tuple x y) = Tuple x (f y)
instance Foldable (Tuple a) where
foldr f z (Tuple _ y) = f y z
instance Traversable (Tuple a) where
traverse f (Tuple x y) = do
y' <- f y
let t' = Tuple x y'
return $ t'
Looks nice! But no:
[1 of 1] Compiling X ( X.hs, interpreted )
X.hs:15:9: error:
• Could not deduce (Monad f) arising from a do statement
from the context: Applicative f
bound by the type signature for:
traverse :: forall (f :: * -> *) a1 b.
Applicative f =>
(a1 -> f b) -> Tuple a a1 -> f (Tuple a b)
at X.hs:14:5-12
Possible fix:
add (Monad f) to the context of
the type signature for:
traverse :: forall (f :: * -> *) a1 b.
Applicative f =>
(a1 -> f b) -> Tuple a a1 -> f (Tuple a b)
• In a stmt of a 'do' block: y' <- f y
In the expression:
do y' <- f y
let t' = Tuple x y'
return $ t'
In an equation for ‘traverse’:
traverse f (Tuple x y)
= do y' <- f y
let t' = ...
return $ t'
|
15 | y' <- f y
| ^^^^^^^^^
Failed, no modules loaded.
Even this fails:
instance Traversable (Tuple a) where
traverse f (Tuple x y) = do
y' <- f y
let unrelated = 1
return $ Tuple x y'
So, introducing any let
statement removes the "applicative" from the "applicative do". Why?
It would translate into
let unrelated = 1 in return $ Tuple x y'
which doesn't have the form return <something>
, and applicative do requires the last statement to be a return
or pure
:
In general, the rule for when a do statement incurs a
Monad
constraint is as follows. If the do-expression has the following form:do p1 <- E1; ...; pn <- En; return E
where none of the variables defined by
p1...pn
are mentioned inE1...En
, andp1...pn
are all variables or lazy patterns, then the expression will only requireApplicative
. Otherwise, the expression will requireMonad
. The block may return a pure expressionE
depending upon the resultsp1...pn
with eitherreturn
orpure
.Note: the final statement must match one of these patterns exactly:
return E return $ E pure E pure $ E
otherwise GHC cannot recognise it as a return statement, and the transformation to use
<$>
that we saw above does not apply. In particular, slight variations such asreturn . Just $ x
orlet x = e in return x
would not be recognised.
If you look at the description of desugaring in https://gitlab.haskell.org/ghc/ghc/wikis/applicative-do, it also doesn't support let
in any way.
What applicative expression would you like this to desugar to? A monadic expression is a series of chained scopes, so it makes sense for a let
to introduce a binding that extends over all the remaining scopes, but with applicative the various expressions can't really depend on each other, so there's no scope that it makes sense to desugar the let
into.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With