abc :: IO (Int)
abc = do
print "abc"
pure $ 10
xyz :: IO (Int)
xyz = undefined
main :: IO ()
main = do
x <- (((+) <$> abc <*> abc) <* xyz)
print x
Почему в приведенном выше описании оценивается xyz
?Я предполагаю, что из-за ленивого характера Хаскелла ему не нужно оценивать xyz
(и, следовательно, не достигать undefined
)?
Мое предположение основано на типе <*
:
Prelude> :t (<*)
(<*) :: Applicative f => f a -> f b -> f a
Далее с:
-- | Sequence actions, discarding the value of the first argument.
(*>) :: f a -> f b -> f b
a1 *> a2 = (id <$ a1) <*> a2
И:
(<$) :: a -> f b -> f a
(<$) = fmap . const
И, следовательно, f b
никогда не используется.
Есть ли способ, которым я могу понять / исследовать, почему это оценивается строго?Было бы полезно посмотреть в скомпилированном ядре GHC?
Благодаря обсуждению в комментариях кажется (пожалуйста, кто-то поправит меня, если я ошибаюсь), это из-за Monad
реализация IO
, потому что следующие два утверждения, кажется, оценивают по-разному:
Идентичность:
runIdentity $ const <$> (pure 1 :: Identity Int) <*> undefined
1
IO:
const <$> (pure 1 :: IO Int) <*> undefined
*** Exception: Prelude.undefined