【问题标题】:Haskell : concat two IO StringsHaskell:连接两个 IO 字符串
【发布时间】:2013-12-17 22:12:58
【问题描述】:

今天我尝试连接两个 IO 字符串,但无法正常工作。

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

更普遍的问题是如何实现更通用的功能(+++) :: IO a -> IO a -> IO a?或者更笼统?

【问题讨论】:

    标签: string function haskell functor


    【解决方案1】:

    你可以从Control.Monad使用liftM2

    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 一起使用,该函数对只有一个参数的函数执行此操作。或者,正如其他人指出的那样,您可以在Control.Applicative 中使用IOApplicative 实例并使用类似的liftA2 函数。

    您可能会注意到泛型Applicatives 在某些情况下与泛型Monads 具有相似的行为,这是因为它们在数学上非常相似。事实上,对于每个Monad,您都可以从中创建一个Applicative。因此,您也可以从每个Applicative 中创建一个Functor。有很多人对 Functor-Applicative-Monad proposal 感到兴奋,它已经存在了一段时间,最终将在即将推出的 GHC 版本中实现。它们形成了一个非常自然的Functor &gt; Applicative &gt; Monad 层次结构。

    【讨论】:

    • 顺便说一句。我找到了另一种解决方案。 (++) &lt;$&gt; s1 &lt;*&gt; s2。它看起来很不错。但现在我真的必须沉思我发现的一切:) 谢谢你的回答。 :)
    • @RottenBrain 该答案使用ApplicativeIO 实例。函数&lt;*&gt;实际上等价于Control.Monad.ap,而&lt;$&gt;只是fmap的中缀版本,它使用了IOFunctor实例。有时在 Haskell 中,数学可以让你以几种不同的方式做某事 =)
    • 你好。我刚刚完成了“Learn You a Haskell”一书,现在我明白了你告诉我的一切。无论如何,谢谢你的帮助。 :)
    • @RottenBrain 很高兴听到这个消息!
    【解决方案2】:
    import Control.Applicative (liftA2)
    
    (+++) :: Applicative f => f [a] -> f [a] -> f [a]
    (+++) = liftA2 (++)
    

    现在在 GHCI 中

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

    【讨论】:

      【解决方案3】:
      (++) <$> pure "stringOne" <*> pure "stringTwo" 
      

      【讨论】:

        【解决方案4】:

        实现函数(+++) ...它的工作原理与(++) :: [a] -&gt; [a] -&gt; [a]完全相同,但对于IO String

        不要这样做,这是个坏主意。连接字符串是一个纯粹的函数操作,没有理由将它放在IO monad 中。除了在你需要结果的地方——我想这可能是在其他一些 IO 中间的某个地方。好吧,那就用do-notation 将读取的字符串绑定到变量名上,然后在上面使用普通的(++)

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

        可以通过写s12 &lt;- liftA2 (++) getS1 getS2 使其更紧凑。但我会在原地做,而不是单独定义。

        对于更长的操作,您当然可能想要定义一个单独的命名操作,但它应该是一个有意义的操作。

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-10-01
          • 2021-10-18
          • 2013-05-01
          • 2011-09-17
          • 1970-01-01
          • 1970-01-01
          • 2013-11-05
          相关资源
          最近更新 更多