Как ведет себя оператор <- в haskell? - PullRequest
0 голосов
/ 04 сентября 2018

Я понимаю (несколько) монады и понимаю, что оператор <- извлечет значение из монады. </p>

Но как это работает с разными типами?

Как правило, я видел, как он используется для извлечения строк из монады ввода-вывода. Но в приведенном ниже примере кода я не могу понять, почему происходит сбой в основной третьей строке, и жалуется, что ожидает тип 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 ]

0 голосов
/ 04 сентября 2018

Будьте осторожны с такими формулировками, как

извлечь значение из монады

Монада не содержит значения 'a'. Например, Maybe содержит ноль или одно значение. Список ([]) содержит несколько значений. См. этот ответ для получения более подробной информации.

В случае списка, например, оператор <- извлекает каждое значений списка, по одному за раз.

Когда вы используете нотацию do, все извлеченные значения должны принадлежать одному и тому же Monad. В OP компилятор делает вывод, что рассматриваемый Monad равен IO, поскольку putStrLn возвращает IO () значений.

logNumber, с другой стороны, возвращает значения Writer [String] Int, и хотя это также экземпляр Monad, он не совпадает с IO. Поэтому код не проверяет тип.

0 голосов
/ 04 сентября 2018

Вещи просты. Это два факта

  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 ответ для полного объяснения

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

0 голосов
/ 04 сентября 2018

Каждый оператор в блоке 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 оставался неизменным.

...