haskell - 如何加入两个 Haskell IO monad
问题描述
以下(工作)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 操作组合成一行?我看了这个类似的问题,但我无法理解答案。
解决方案
(>>=)
是“规范”的单子运算符,如 Robin Zigmond 的回答中所给出的。但是,如果您尝试以类似应用程序的风格编写代码,我经常喜欢使用它的翻转版本,(=<<)
. 它与 Functor 和 Applicative 中的函数具有很好的对称性,并且它们与普通的非单子函数调用非常相似,只是插入了一个额外的运算符:
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)
?
推荐阅读
- java - 如何延迟加载所有 Spring bean,无论它是由 Springboot 2.2 中的 @Bean 还是 @Component 定义的
- docker - 将使用 Docker 容器运行的网站从 HTTP 转换为 HTTPS
- android - Android:Okhttp3:SocketTimeoutException:无法连接到 dns/xx.xx.xx.xx(端口 443)
- php - 由于错误“cURL 错误 60:SSL 证书问题:无法获取本地颁发者证书”而无法获取文件
- angular - 为什么 Angular 在其源代码中使用静态类而不是导出函数?
- c++ - 如何静态制作 CMake 或 OpenCV 链接 [交叉编译 Linux -> Windows]
- plot - 重读命令使 gnuplot 中的动画失真
- php - PHP变量不显示在回声中
- php - foreach 循环中的文件仅包含一次
- vcloud-director-rest-api - vCloudDirector API 无法更改 vApp 的实例化参数