Чтобы понять эти типы, полезно взглянуть на окружающие функции.
Ошибка упоминает переменную k
, которая сначала появляется в выражении callCC $ \k -> forever ...
. Мы можем получить тип k, посмотрев на тип callCC
:
callCC :: MonadCont m => ((a -> m b) -> m a) -> m a
Из этого видно, что k
имеет тип a -> m b
. Обратите внимание, что поскольку b
не используется где-либо еще в этой функции, его тип не имеет значения и будет определяться контекстом, в котором используется функция.
k
используется в выражении when после $
(которое на самом деле не нужно). Тип, когда это:
when :: Monad m => Bool -> m () -> m ()
Обратите внимание, что второй аргумент ожидает m ()
, но вы передаете k, которое имеет тип a -> m b
(так как b
не имеет значения, оно может соответствовать ()
. Поэтому, очевидно, необходимо указать какой-то аргумент на k
. Чтобы выяснить, что это, мы оглянемся на определение callCC. Этот аргумент является значением forever $ do ...
в вашей программе.
Глядя на тип вечности:
forever :: Monad m => m a -> m b
Требуется одно монадическое вычисление m a
, и в результате возвращается другое монадическое вычисление m b
. Обратите внимание, что b
не появляется в аргументах forever
. Это означает, что тип определяется контекстом, в котором он вызывается (например, read "3"
может иметь тип Double
или Int
в зависимости от выражения, в котором он находится). Это определяется runContT
:
runContT :: ContT r m a -> (a -> m r) -> m r
Если вы сопоставите переменные типа из runContT
, callCC
и forever
, вы заметите, что b
в forever
соответствует a
в runContT
. a
используется во втором аргументе runContT
, который в вашей программе равен return
. return
имеет тип a -> m a
, поэтому тип a
совпадает с r
в вашей программе. r
появляется на выходе m r
.
Выражение runContT
находится в контексте do, без каких-либо привязок (<-
). Ваш код эквивалентен этому:
main = do
rn <- randomRIO (1,10) :: IO Int
runContT (callCC ....) (return) >> (putStrLn $ "Good guess! " ++ (show rn))
Тайна, наконец, будет раскрыта, если взглянуть на тип >>
:
(>>) :: Monad m => m a -> m b -> m b
>>
отбрасывает значение первого передаваемого ему монадического вычисления (которое было выражением runContT
). Таким образом, значение, которое возвращает вычисление, на самом деле не имеет значения (обратите внимание, что a
не появляется в результате функции >>
). Если вы проследите за последствиями этого объяснения, вы поймете, что переменная, переданная в k
, на самом деле не имеет значения! Если вы передадите ему что-нибудь, функция будет работать правильно:
import System.Random
import Control.Monad
import Control.Monad.Cont
main = do
rn <- randomRIO (1,10) :: IO Int
runContT (callCC $ \k -> forever $ do
lift $ putStr "Your number:"
n <- lift (fmap read getLine)
when (n == rn) $ k (Just ("Seriously anything works here", 42, [42..]))
lift $ putStrLn $ if (n > rn)
then "Too much"
else "Too little") (return)
putStrLn $ "Good guess! " ++ (show rn)
Так что это был действительно сложный пример, и я понимаю, почему вы не следовали ему. Вы становитесь лучше с опытом, хотя. Кроме того, монада продолжения довольно продвинута и сложна для haskell.