Как ведет себя оператор <- в haskell?
Я понимаю (в некоторой степени) монады и понимаю, что оператор <- извлечет значение из монады.
Но как это работает с разными типами?
Как правило, я видел, как он используется для извлечения строк из монады ввода-вывода. Но в приведенном ниже примере кода я не могу понять, почему происходит сбой в основной третьей строке, и жалуется, что ожидает тип IO int? Как компилятор делает вывод, что необходим IO int?
И что это делает (<-
) сделать в multWithLog
метод?
import Control.Monad.Trans.Writer.Lazy
main = do
putStrLn $ show $ logNumber 3 -- prints WriterT (Identity (3,["Got Number: 3"]))
putStrLn $ show $ multWithLog -- prints WriterT (Identity (3,["Got Number: 3"]))
_ <- logNumber 3 -- fails with Couldn't match type ‘WriterT [String] Data.Functor.Identity.Identity’ with ‘IO’
-- Expected type: IO Int
-- Actual type: Writer [String] Int
putStrLn "test"
logNumber :: Int -> Writer [String] Int
logNumber x = writer (x, ["Got Number: " ++ show x])
multWithLog :: Writer [String] Int
multWithLog = do
a <- logNumber 3
--b <- logNumber 5
return a
3 ответа
Каждое утверждение в do
блок должен быть из того же монадического типа.
В
multWithLog = do
a <- logNumber 3
return a
у нас есть logNumber 3 :: Writer [String] Int
а также return a :: (Monad m) => m Int
(что является полиморфным), так что все это проверяется как Writer [String] Int
(с m = Writer [String]
, которая является монадой).
В
main = do
putStrLn $ show $ logNumber 3
putStrLn $ show $ multWithLog
_ <- logNumber 3
putStrLn "test"
у нас есть putStrLn ... :: IO ()
а также logNumber 3 :: Writer [String] Int
, Это ошибка типа, потому что Writer [String]
это не то же самое, что IO
,
Основная причина заключается в том, что do
блоки просто синтаксический сахар для звонков >>=
а также >>
, Например, ваш main
действительно означает
main =
(putStrLn $ show $ logNumber 3) >>
(putStrLn $ show $ multWithLog) >>
logNumber 3 >>= \_ ->
putStrLn "test"
с
(>>) :: (Monad m) => m a -> m b -> m b
(>>=) :: (Monad m) => m a -> (a -> m b) -> m b
который требует типа m
оставаться неизменным во всем.
Будьте осторожны с такими формулировками, как
извлечь значение из монады
Монада не содержит значения "а". Например, Maybe
содержит ноль или одно значение. Список ([]
) содержит несколько значений. Смотрите этот ответ для более подробной информации.
В случае списка, например, <-
Оператор извлекает каждое из значений списка, по одному за раз.
Когда вы используете do
нотация, все извлеченные значения должны принадлежать одному Monad
, В OP компилятор делает вывод, что Monad
речь идет IO
, поскольку putStrLn
возвращается IO ()
ценности.
logNumber
с другой стороны, возвращает Writer [String] Int
ценности, и в то время как это также Monad
Например, это не то же самое, что IO
, Поэтому код не проверяет тип.
Держать вещи простыми. Это два факта
Writer [String]
на самом деле монада, такWriter [String] Int
можно рассматривать какm Int
- Каждое действие в
do
блок должен происходить в одной и той же монаде.
в main
Функция компилятора рассуждает следующим образом:
- Я работаю в
IO
монада сputStrLn ...
имеет типIO ()
- Позвольте мне вычислить
_ <- logNumber 3
, Так как я вIO
монада,logNumber 3
должно бытьIO WhatEver
logNumber 3
на самом деле монадическое значение типаm Int
- Подождите!
m
этоWriter [String]
монада, а неIO
монада - Напечатайте ошибку о том, что
Writer [String] Int
неверноIO Int
Так вот где IO Int
происходит от. Я просто пытаюсь быть педагогом здесь. Проверьте @melpomene ответ для полного объяснения
Надеюсь, поможет.