I want to achieve the following in a do block:
do
if condition then
n0 <- expr0
else
n0 <- expr0'
n1 <- expr1
n2 <- expr2
return T n0 n1 n2
But Haskell gives a compile error unless I do:
do
if condition then
n0 <- expr0
n1 <- expr1
n2 <- expr2
return T n0 n1 n2
else
n0 <- expr0'
n1 <- expr1
n2 <- expr2
return T n0 n1 n2
It looks very verbose, especially when there are many shared binding expressions. How do I make it more concise?
Actually, I am trying to do the following:
do
if isJust maybeVar then
n0 <- f (fromJust maybeVar)
n1 <- expr1
n2 <- expr2
return (T (Just n0) n1 n2)
else
n1 <- expr1
n2 <- expr2
return (T Nothing n1 n2)
The following still fails to compile:
do
n0 <- if isJust maybeVar then Just (f (fromJust maybeVar)) else Nothing
n1 <- expr1
n2 <- expr2
return (T n0 n1 n2)
You can "inline" the condition:
do
n0 <- if condition then expr0 else expr0'
n1 <- expr1
n2 <- expr2
return (T n0 n1 n2)
You likely should use brackets in the return
expression, so return (T n0 n1 n2)
.
You can then rewrite the expression with liftM3 :: Monad m => (a1 -> a2 -> a3 -> r) -> m a1 -> m a2 -> m a3 -> m r
to:
liftM3 T (if condition then expr0 else expr0') expr1 expr2
Since Haskell is a pure language evaluating expressions has no side-effects. But here the if
...then
...else
will at most evaluate one of the two expression. An IO a
itself has no side-effects either, since it is a "recipe" to generate an a
.
EDIT: For your second example, it is more complicated.
do
n0 <- if isJust maybeVar then Just <$> (f (fromJust maybeVar)) else pure Nothing
n1 <- expr1
n2 <- expr2
return (T n0 n1 n2)
So here we use pure Nothing
to "wrap" Nothing
in the monadic context, and Just <$>
to apply Just
on the value(s) inside the monadic context.
Or as @luqui says, we can here use traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
:
do
n0 <- traverse f maybeVar
n1 <- expr1
n2 <- expr2
return (T n0 n1 n2)
This works since a Maybe
is traversable: for a Just
we traverse the single element, for Nothing
it will return Nothing
.