Почему импорт Control.Applicative позволяет этому плохому коду вводить проверку?

Я помогаю другу узнать Haskell, и он недавно создал такой код, который проверяет и создает цикл сжигания CPU во время выполнения. Я полностью озадачен этим.

import Control.Monad
import Control.Applicative

main = forever putStrLn "Hello, infinity"

Это не должно вводить проверку, но делает. Правильная версия будет выглядеть следующим образом:

main = forever $ putStrLn "Hello, infinity"

Что странно и удивительно для меня, так это то, что вы получаете разные результаты и без импорта Control.Applicative. Не импортируя его, он не печатает проверку:

Prelude Control.Monad> forever putStrLn "Hello, infinity"

<interactive>:1:1:
    No instance for (Monad ((->) String))
      arising from a use of `forever'
    Possible fix: add an instance declaration for (Monad ((->) String))
    In the expression: forever putStrLn "Hello, infinity"
    In an equation for `it': it = forever putStrLn "Hello, infinity"

Я не вижу экземпляр Monad для ((->) String в источнике для Control.Applicative, поэтому я предполагаю, что что-то странное происходит из-за использования Control.Category или Control.Arrow, но я не знать. Поэтому у меня есть два вопроса:

  • Что происходит с импортом Control.Applicative, который позволяет это произойти?
  • Что происходит, когда он входит в бесконечный цикл? Что Haskell на самом деле пытается выполнить в этом случае?

Спасибо,

Ответ 1

Нет экземпляра для (->) String, но есть экземпляр для (->) e... и этот экземпляр очень и очень полезен во многих ситуациях. Во втором вопросе мы должны взглянуть на forever и экземпляр класса для функций:

instance Monad ((->) e) where
    return x = \e -> x
    m >>= f  = \e -> f (m e) e

forever m = m >> forever m = m >>= \_ -> forever m

Теперь, что делает forever putStrLn?

forever putStrLn
    = putStrLn >>= \_ -> forever putStrLn
    = \e -> (\_ -> forever putStrLn) (putStrLn e) e
    = \e -> (forever putStrLn) e
    = forever putStrLn

... это просто чистый бесконечный цикл, в основном идентичный loop = loop.

Чтобы получить некоторую интуицию для того, что происходит с монадой читателя (как известно), посмотрите документацию, Все О разделе Monads в разделе Reader, и есть несколько намеков, накрытых на Typeclassopedia, которые могут помочь.

Ответ 2

Control.Applicative импортирует Control.Monad.Instances и, следовательно, реэкспортирует экземпляры из Control.Monad.Instances. Это включает Functor и Monad экземпляры для ((->) r).