为什么类型在下一行匹配,但在 `do` 块中不在同一行?
Why does the type match on the next line but not on the same line in `do` block?
我正在阅读用户必须键入的输入中的几行:
main :: IO ()
main = do
let size = 3
arr <- replicateM size getLine
let pairs = map parsePair arr
print pairs
为什么我可以在单独的行上执行 map parsePair arr
但不在同一条线上,像这样:
arr <- map parsePair (replicateM size getLine)
这样做,我得到错误:
• Couldn't match type ‘[]’ with ‘IO’
Expected type: IO [Int]
Actual type: [[Int]]
为了给你更多的细节,这里是parsePair
:
parsePair string = map parseInt $ words string
parseInt :: String -> Int
parseInt s = read s :: Int
因为replicateM size getLine
的类型是<b>IO</b>[String]
,所以不是[=11=的列表]s,它基本上是对将获得 [String]
的 IO
操作的描述。您可以在 IO
monad 中看到箭头 <-
作为检索它并解压缩结果的一种方式。
但是你可以做一些处理,因为 IO
也是一个 Functor
,你可以使用 fmap :: Functor f => (a -> b) -> f a -> f b
:
main :: IO [Int]
main = do
let size = 3
<b>fmap (map parsePair)</b> (replicateM size getLine)
或者您可以将 fmap
移动到 getLine
部分:
main :: IO [Int]
main = do
let size = 3
replicateM size (<b>fmap parsePair</b> getLine)
请注意,有一个 readLn :: Read a => IO a
函数基本上是 fmap read getLine
(除了它执行一些额外的错误处理)。因此我们可以使用:
main :: IO [Int]
main = do
let size = 3
replicateM size <b>readLn</b>
请注意,带有 <-
符号的 do
语法确实不像赋值运算符(在许多语言中是 =
,有时 :=
有时确实<-
,例如在 R) 中。相反,它是一个特殊的构造,它 执行一个单子动作 并提取此动作的 结果 。一个动作和它的结果之间有一个根本的区别;经常被引用的类比是,一个动作就像蛋糕的食谱,结果就是蛋糕本身。
直接将map parsePair
应用到IO
动作就像拿刀把菜谱切成碎片,然后期望你可以用那个菜谱烤一个现成的蛋糕。显然这不是它的工作原理。
同样,您首先需要执行 (bind) 一个 IO
操作,然后才能操作结果。这就是 arr <- replicateM size getLine
行发生的事情:执行操作并且仅将其结果存储在 arr
中,这样您就可以编写 map parsePair arr
.
或者,您可以使用 fmap
运算符。它所做的基本上是,它需要一个配方和一些如何处理结果的指令,然后将该指令添加到配方的末尾。如果再执行那个菜谱,结果确实是切块的蛋糕。
我正在阅读用户必须键入的输入中的几行:
main :: IO ()
main = do
let size = 3
arr <- replicateM size getLine
let pairs = map parsePair arr
print pairs
为什么我可以在单独的行上执行 map parsePair arr
但不在同一条线上,像这样:
arr <- map parsePair (replicateM size getLine)
这样做,我得到错误:
• Couldn't match type ‘[]’ with ‘IO’
Expected type: IO [Int]
Actual type: [[Int]]
为了给你更多的细节,这里是parsePair
:
parsePair string = map parseInt $ words string
parseInt :: String -> Int
parseInt s = read s :: Int
因为replicateM size getLine
的类型是<b>IO</b>[String]
,所以不是[=11=的列表]s,它基本上是对将获得 [String]
的 IO
操作的描述。您可以在 IO
monad 中看到箭头 <-
作为检索它并解压缩结果的一种方式。
但是你可以做一些处理,因为 IO
也是一个 Functor
,你可以使用 fmap :: Functor f => (a -> b) -> f a -> f b
:
main :: IO [Int]
main = do
let size = 3
<b>fmap (map parsePair)</b> (replicateM size getLine)
或者您可以将 fmap
移动到 getLine
部分:
main :: IO [Int]
main = do
let size = 3
replicateM size (<b>fmap parsePair</b> getLine)
请注意,有一个 readLn :: Read a => IO a
函数基本上是 fmap read getLine
(除了它执行一些额外的错误处理)。因此我们可以使用:
main :: IO [Int]
main = do
let size = 3
replicateM size <b>readLn</b>
请注意,带有 <-
符号的 do
语法确实不像赋值运算符(在许多语言中是 =
,有时 :=
有时确实<-
,例如在 R) 中。相反,它是一个特殊的构造,它 执行一个单子动作 并提取此动作的 结果 。一个动作和它的结果之间有一个根本的区别;经常被引用的类比是,一个动作就像蛋糕的食谱,结果就是蛋糕本身。
直接将map parsePair
应用到IO
动作就像拿刀把菜谱切成碎片,然后期望你可以用那个菜谱烤一个现成的蛋糕。显然这不是它的工作原理。
同样,您首先需要执行 (bind) 一个 IO
操作,然后才能操作结果。这就是 arr <- replicateM size getLine
行发生的事情:执行操作并且仅将其结果存储在 arr
中,这样您就可以编写 map parsePair arr
.
或者,您可以使用 fmap
运算符。它所做的基本上是,它需要一个配方和一些如何处理结果的指令,然后将该指令添加到配方的末尾。如果再执行那个菜谱,结果确实是切块的蛋糕。