abc :: IO (Int)
abc = do
print "abc"
pure $ 10
xyz :: IO (Int)
xyz = undefined
main :: IO ()
main = do
x <- (((+) <$> abc <*> abc) <* xyz)
print x
Why in the above is xyz
being evaluated? I would assume due to Haskell's lazy nature it would not need to evaluate xyz
(and hence not reach the undefined
)?
My assumption is based on the type of <*
:
Prelude> :t (<*)
(<*) :: Applicative f => f a -> f b -> f a
Following on with:
-- | Sequence actions, discarding the value of the first argument.
(*>) :: f a -> f b -> f b
a1 *> a2 = (id <$ a1) <*> a2
And:
(<$) :: a -> f b -> f a
(<$) = fmap . const
And hence f b
never gets used.
Is there a way I can understand / investigate why this is being evaluated strictly? Would looking at the GHC compiled Core be helpful in this?
Thanks to the discussion in the comments it seems (please someone correct me if I'm wrong) it's due to the Monad
implementation of the IO
because the following two statements seem to evaluate differently:
Identity:
runIdentity $ const <$> (pure 1 :: Identity Int) <*> undefined
1
IO:
const <$> (pure 1 :: IO Int) <*> undefined
*** Exception: Prelude.undefined