如何连接两个 Haskell IO monad
How to join two Haskell IO monads
以下(有效)Haskell 程序输出随机拼写:
import System.Random
spells =
[ "Abracadabra!"
, "Hocus pocus!"
, "Simsalabim!"
]
main :: IO()
main = do
spell <- (spells !!) <$> randomRIO (0, length spells - 1)
putStrLn spell
然而,变量spell
是非常无用的。它存储从法术列表中选择的随机字符串,但随后立即传递给 putStrLn
函数并且不再使用。我试图将两个 IO 操作组合成一行,如下所示:
main = putStrLn <$> (spells !!) <$> randomRIO (0, length spells - 1)
但是我得到了以下错误:
• Couldn't match type ‘IO ()’ with ‘()’
Expected type: Int -> ()
Actual type: Int -> IO ()
• In the first argument of ‘(<$>)’, namely
‘putStrLn <$> (spells !!)’
In the expression:
putStrLn <$> (spells !!) <$> randomRIO (0, length spells - 1)
In an equation for ‘main’:
main
= putStrLn <$> (spells !!) <$> randomRIO (0, length spells - 1)
|
160 | main = putStrLn <$> (spells !!) <$> randomRIO (0, length spells - 1)
| ^^^^^^^^^^^^^^^^^^^^^^^^
有没有办法把两个IO操作合并成一行?我看了 this similar question 但我无法理解答案。
do
符号只是使用 bind 运算符 (>>=
) 的语法糖。所以你的 2 行 do
块可以重写:
main = do
spells <- (spells !!) <$> randomRIO (0, length spells - 1)
putStrLn spells
-- rewrite to bind notation
main = ((spells !!) <$> randomRIO (0, length spells - 1)) >>= \spells ->
putStrLn spells
-- simplify the lambda
main = ((spells !!) <$> randomRIO (0, length spells - 1)) >>= putStrLn
但是我会质疑在这里这样做是否有任何可读性或其他好处。
(>>=)
是 Robin Zigmond 的回答中给出的“规范”单子运算符。但是,如果您尝试以 applicative-like 风格编写代码,我通常喜欢使用它的翻转版本 (=<<)
。它与 Functor 和 Applicative 中的函数有很好的对称性,以及它们如何类似于普通的 non-monadic 函数调用,只是插入了一个额外的运算符:
f x -- one-argument function call
f <$> fx -- fmapping that function into a functor
g x y -- two-argument function call
g <$> ax <*> ay -- applied over two applicatives
f =<< mx -- binding a function with a monadic value
mx >>= f -- looks backwards, doesn't it?
所以你的表达式可以写成
main = putStrLn =<< (spells !!) <$> randomRIO (0, length spells - 1)
就我个人而言,我宁愿使用更多的普通函数组合和更少的上下文映射,所以我将 (spells !!)
移动到绑定运算符的左侧:
main = putStrLn . (spells !!) =<< randomRIO (0, length spells - 1)
看看这样的顺序读起来怎么样? “打印出 randomRIO (0, length spells - 1)
给出的索引处的咒语”?
以下(有效)Haskell 程序输出随机拼写:
import System.Random
spells =
[ "Abracadabra!"
, "Hocus pocus!"
, "Simsalabim!"
]
main :: IO()
main = do
spell <- (spells !!) <$> randomRIO (0, length spells - 1)
putStrLn spell
然而,变量spell
是非常无用的。它存储从法术列表中选择的随机字符串,但随后立即传递给 putStrLn
函数并且不再使用。我试图将两个 IO 操作组合成一行,如下所示:
main = putStrLn <$> (spells !!) <$> randomRIO (0, length spells - 1)
但是我得到了以下错误:
• Couldn't match type ‘IO ()’ with ‘()’
Expected type: Int -> ()
Actual type: Int -> IO ()
• In the first argument of ‘(<$>)’, namely
‘putStrLn <$> (spells !!)’
In the expression:
putStrLn <$> (spells !!) <$> randomRIO (0, length spells - 1)
In an equation for ‘main’:
main
= putStrLn <$> (spells !!) <$> randomRIO (0, length spells - 1)
|
160 | main = putStrLn <$> (spells !!) <$> randomRIO (0, length spells - 1)
| ^^^^^^^^^^^^^^^^^^^^^^^^
有没有办法把两个IO操作合并成一行?我看了 this similar question 但我无法理解答案。
do
符号只是使用 bind 运算符 (>>=
) 的语法糖。所以你的 2 行 do
块可以重写:
main = do
spells <- (spells !!) <$> randomRIO (0, length spells - 1)
putStrLn spells
-- rewrite to bind notation
main = ((spells !!) <$> randomRIO (0, length spells - 1)) >>= \spells ->
putStrLn spells
-- simplify the lambda
main = ((spells !!) <$> randomRIO (0, length spells - 1)) >>= putStrLn
但是我会质疑在这里这样做是否有任何可读性或其他好处。
(>>=)
是 Robin Zigmond 的回答中给出的“规范”单子运算符。但是,如果您尝试以 applicative-like 风格编写代码,我通常喜欢使用它的翻转版本 (=<<)
。它与 Functor 和 Applicative 中的函数有很好的对称性,以及它们如何类似于普通的 non-monadic 函数调用,只是插入了一个额外的运算符:
f x -- one-argument function call
f <$> fx -- fmapping that function into a functor
g x y -- two-argument function call
g <$> ax <*> ay -- applied over two applicatives
f =<< mx -- binding a function with a monadic value
mx >>= f -- looks backwards, doesn't it?
所以你的表达式可以写成
main = putStrLn =<< (spells !!) <$> randomRIO (0, length spells - 1)
就我个人而言,我宁愿使用更多的普通函数组合和更少的上下文映射,所以我将 (spells !!)
移动到绑定运算符的左侧:
main = putStrLn . (spells !!) =<< randomRIO (0, length spells - 1)
看看这样的顺序读起来怎么样? “打印出 randomRIO (0, length spells - 1)
给出的索引处的咒语”?