首页 > 解决方案 > 如何加入两个 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 操作组合成一行?我看了这个类似的问题,但我无法理解答案。

标签: haskellrandommonadsio-monad

解决方案


(>>=)是“规范”的单子运算符,如 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)


推荐阅读