Haskell:连接两个IO字符串

时间:2013-12-17 22:12:59

标签: string function haskell functor

今天我试图连接两个IO字符串并且无法使其工作。

所以,问题是:假设我们有s1 :: IO Strings2 :: IO String。如何实现函数(+++) :: IO String -> IO String -> IO String,它与(++) :: [a] -> [a] -> [a]完全一样,但对于IO String?

更一般的问题是如何实现更一般的函数(+++) :: IO a -> IO a -> IO a?或者甚至更一般?

4 个答案:

答案 0 :(得分:17)

您可以使用liftM2中的Control.Monad

liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c


> :t liftM2 (++)
liftM2 (++) :: Monad m => m [a] -> m [a] -> m [a]

或者,您可以使用do表示法:

(+++) :: Monad m => m [a] -> m [a] -> m [a]
ms1 +++ ms2 = do
    s1 <- ms1
    s2 <- ms2
    return $ s1 ++ s2

这两者都是等价的。实际上,liftM2的定义实现为

liftM2 :: Monad m => (a -> b -> c) -> m a -> m b -> m c
liftM2 f m1 m2 = do
    val1 <- m1
    val2 <- m2
    return $ f val1 val2

很简单!它所做的就是从两个monadic动作中提取值,并为它们应用2个参数的函数。这与函数liftM一起使用,该函数仅对一个参数的函数执行此操作。或者,正如其他人所指出的,您可以在IO中使用Applicative的{​​{1}}个实例,并使用类似的Control.Applicative函数。

您可能会注意到泛型liftA2在某些情况下与通用Applicative具有相似的行为,其原因是因为它们在数学上非常相似。事实上,对于每个Monad,您都可以使用Monad。因此,您还可以在每个Applicative中生成Functor。有很多人对Functor-Applicative-Monad proposal已经存在一段时间感到兴奋,并且最终将在即将推出的GHC版本中实施。它们构成了Applicative的非常自然的层次结构。

答案 1 :(得分:5)

import Control.Applicative (liftA2)

(+++) :: Applicative f => f [a] -> f [a] -> f [a]
(+++) = liftA2 (++)

现在在GHCI

>> getLine +++ getLine
Hello <ENTER>
World!<ENTER>
Hello World!

答案 2 :(得分:4)

(++) <$> pure "stringOne" <*> pure "stringTwo" 

答案 3 :(得分:3)

  

实现函数(+++) ...与(++) :: [a] -> [a] -> [a]完全一样,但对于IO String

不要这样做,这是个坏主意。连接字符串是一个纯函数操作,没有理由在IO monad中使用它。除了你需要结果的地方 - 我想在其他IO的中间某个地方。那么,只需使用do - 表示法将读取的字符串绑定到变量名称,并在它们上使用普通的(++)

do
  print "Now start obtaining strings..."
  somePreliminaryActions
  someMoreIOStuff
  s1 <- getS1
  s2 <- getS2
  yetMoreIO
  useConcat'dStrings (s1 ++ s2)
  print "Done."

通过编写s12 <- liftA2 (++) getS1 getS2可以使其更加紧凑。但我会这样做,而不是单独定义。

对于较长时间的操作,您当然可能希望定义一个单独的命名操作,但它应该是一个有意义的操作。

您不应将IO String个对象视为“IO-strings”。它们不是,就像[Int]不是“列表整数”一样。 IO String类型的对象是操作,在发生时,可以在String monad中提供IO对象。它本身不是一个字符串。