Хаскелл путает с ContT, callCC, когда - PullRequest
4 голосов
/ 09 февраля 2010

Продолжение квеста, чтобы понять ContT и друзей. Пожалуйста, рассмотрите (абсурдный, но иллюстративный) код ниже:

v :: IO (Either String [String])
v = return $ Left "Error message"

doit :: IO (Either String ())
doit = (flip runContT return) $ callCC $ \k -> do
    x <- liftIO $ v
    x2 <- either (k . Left) return x
    when True $ k (Left "Error message 2")
    -- k (Left "Error message 3")
    return $ Right () -- success

Этот код не компилируется. Однако, если заменить when на комментарий с комментарием k под ним, он компилируется. Что происходит?

В качестве альтернативы, если я закомментирую строку x2, она также скомпилируется. ???

Очевидно, что это искаженная версия исходного кода, поэтому все элементы служат определенной цели. Спасибо за разъяснения о том, что происходит и как это исправить. Спасибо.

1 Ответ

6 голосов
/ 09 февраля 2010

Проблема здесь связана с типами when и either, не относящимися к ContT:

when :: forall (m :: * -> *). (Monad m) => Bool -> m () -> m ()
either :: forall a c b. (a -> c) -> (b -> c) -> Either a b -> c

Второй аргумент должен иметь тип m () для некоторой монады m. Таким образом, строка when вашего кода может быть изменена следующим образом:

when True $ k (Left "Error message 2") >> return ()

для компиляции кода. Это, вероятно, не то, что вы хотите сделать, но оно дает нам подсказку о том, что может быть неправильным: тип k был выведен как нечто неприятное для when.

Теперь для подписи either: обратите внимание, что два аргумента either должны быть функциями, которые выдают результаты одного типа. Тип return здесь определяется типом x, который в свою очередь фиксируется явной подписью на v. Таким образом, бит (k . Left) должен иметь одинаковый тип; это в свою очередь исправляет тип k at (определяется GHC)

k :: Either String () -> ContT (Either String ()) IO [String]

Это несовместимо с ожиданиями when.

Однако, когда вы закомментируете строку x2, ее влияние на представление кода в коде проверки типа исключается, поэтому k больше не переводится в неудобный тип и может свободно принимать тип

k :: Either [Char] () -> ContT (Either [Char] ()) IO ()

, что хорошо в книге when. Таким образом, код компилируется.

В качестве заключительного замечания я использовал средство контрольных точек GHCi для получения точных типов k в двух сценариях - я далеко не настолько опытен, чтобы выписывать их вручную и быть в какой-либо степени уверен в их правильности , :-) Используйте :break ModuleName line-number column-number, чтобы попробовать.

...