haskell 中的运算符 <- 的行为如何?
How does the operator <- in haskell behave?
我(有点)了解 monad 并了解运算符 <- 将从 monad 中提取值。
但是它如何处理不同的类型?
通常,我看到它被用来从 IO monad 中提取字符串。但是在下面的示例代码中,我看不出为什么它在第 3 行主线失败,抱怨它期待一种 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
do
块中的每个语句都必须来自相同的 monadic 类型。
在
multWithLog = do
a <- logNumber 3
return a
我们有 logNumber 3 :: Writer [String] Int
和 return a :: (Monad m) => m Int
(它是多态的),所以整个东西类型检查为 Writer [String] Int
(m = Writer [String]
,这是一个 monad)。
在
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
始终保持不变。
注意
等措辞
extract the value from the monad
Monad 不包含 'a' 值。例如,Maybe
包含零个或一个值。列表 ([]
) 包含多个值。有关详细信息,请参阅 。
例如,在列表情况下,<-
运算符一次提取 每个 个列表值。
当您使用 do
表示法时,提取的所有值必须属于同一个 Monad
。在 OP 中,编译器推断有问题的 Monad
是 IO
,因为 putStrLn
returns IO ()
值。
logNumber
,另一方面,returns Writer [String] Int
值,虽然这也是一个 Monad
实例,但它与 IO
不同.因此,代码不进行类型检查。
让事情变得简单。这是两个事实
Writer [String]
实际上是monad,所以Writer [String] Int
可以看作m Int
do
块中的每个动作都应该在同一个 monad 中发生。
在 main
函数中,编译器推理如下:
- 我在
IO
monad 中工作,因为 putStrLn ...
是 IO ()
类型
- 让我计算一下
_ <- logNumber 3
。因为我在 IO
monad 中,所以 logNumber 3
应该是 IO WhatEver
logNumber 3
实际上是 m Int
类型的单子值
- 等等!
m
是 Writer [String]
monad,而不是 IO
monad
- 打印错误说
Writer [String] Int
不正确应该是IO Int
这就是 IO Int
的来源。我只是想在这里成为一名教师。查看@melpomene 的回答以获得完整的解释
希望对您有所帮助。
我(有点)了解 monad 并了解运算符 <- 将从 monad 中提取值。
但是它如何处理不同的类型?
通常,我看到它被用来从 IO monad 中提取字符串。但是在下面的示例代码中,我看不出为什么它在第 3 行主线失败,抱怨它期待一种 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
do
块中的每个语句都必须来自相同的 monadic 类型。
在
multWithLog = do
a <- logNumber 3
return a
我们有 logNumber 3 :: Writer [String] Int
和 return a :: (Monad m) => m Int
(它是多态的),所以整个东西类型检查为 Writer [String] Int
(m = Writer [String]
,这是一个 monad)。
在
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
始终保持不变。
注意
等措辞extract the value from the monad
Monad 不包含 'a' 值。例如,Maybe
包含零个或一个值。列表 ([]
) 包含多个值。有关详细信息,请参阅
例如,在列表情况下,<-
运算符一次提取 每个 个列表值。
当您使用 do
表示法时,提取的所有值必须属于同一个 Monad
。在 OP 中,编译器推断有问题的 Monad
是 IO
,因为 putStrLn
returns IO ()
值。
logNumber
,另一方面,returns Writer [String] Int
值,虽然这也是一个 Monad
实例,但它与 IO
不同.因此,代码不进行类型检查。
让事情变得简单。这是两个事实
Writer [String]
实际上是monad,所以Writer [String] Int
可以看作m Int
do
块中的每个动作都应该在同一个 monad 中发生。
在 main
函数中,编译器推理如下:
- 我在
IO
monad 中工作,因为putStrLn ...
是IO ()
类型
- 让我计算一下
_ <- logNumber 3
。因为我在IO
monad 中,所以logNumber 3
应该是IO WhatEver
logNumber 3
实际上是m Int
类型的单子值
- 等等!
m
是Writer [String]
monad,而不是IO
monad - 打印错误说
Writer [String] Int
不正确应该是IO Int
这就是 IO Int
的来源。我只是想在这里成为一名教师。查看@melpomene 的回答以获得完整的解释
希望对您有所帮助。