Как ведет себя оператор <- в 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] Intm = 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, Поэтому код не проверяет тип.

Держать вещи простыми. Это два факта

  1. Writer [String] на самом деле монада, так Writer [String] Int можно рассматривать как m Int
  2. Каждое действие в do блок должен происходить в одной и той же монаде.

в main Функция компилятора рассуждает следующим образом:

  1. Я работаю в IO монада с putStrLn ... имеет тип IO ()
  2. Позвольте мне вычислить _ <- logNumber 3, Так как я в IO монада, logNumber 3 должно быть IO WhatEver
  3. logNumber 3 на самом деле монадическое значение типа m Int
  4. Подождите! m это Writer [String] монада, а не IO монада
  5. Напечатайте ошибку о том, что Writer [String] Int неверно IO Int

Так вот где IO Int происходит от. Я просто пытаюсь быть педагогом здесь. Проверьте @melpomene ответ для полного объяснения

Надеюсь, поможет.

Другие вопросы по тегам