7

我对 Haskell 相当陌生,并且一直在尝试找到一种方法将多个受 IO 污染的值传递给处理 C 库的函数。大多数人似乎在 do 块中使用 <- 运算符,如下所示:

g x y = x ++ y
interactiveConcat1 = do {x <- getLine;
                         y <- getLine;
                         putStrLn (g x y);
                         return ()}

这让我觉得我在做 C,除了 emacs 不能自动缩进。我试着用更 Lispy 的风格来写这个:

interactiveConcat2 = getLine >>= (\x ->
                     getLine >>= (\y ->
                     putStrLn (g x y) >>
                     return () ))

这看起来像一团糟,并且有一串闭括号,你必须在最后数数(除此之外,emacs 可以在 Lisp 中可靠地协助完成这项任务,但在 Haskell 中则不行)。还有一种说法是

import Control.Applicative
interactiveConcat3 = return g <*> getLine <*> getLine >>= putStrLn

它看起来很整洁,但不是基础语言的一部分。

从 IO 污点盒中剥离值是否有任何不那么费力的符号?也许有更清洁的方式使用 lift* 或 fmap?我希望不要太主观地问什么被认为是“惯用的”?

此外,任何使 emacs 比(Haskell Ind)模式更好地协作的技巧将不胜感激。谢谢!

约翰

编辑:我偶然发现了https://wiki.haskell.org/Do_notation_considered_harmful并意识到我写的 lambda 链中的嵌套括号是不必要的。然而,社区(和 ghc 实现者)似乎已经接受了 Applicative-inspired 风格,使用 、<*> 等,这似乎使代码更容易阅读,尽管在弄清楚运算符优先级方面很头疼。

4

2 回答 2

14

注意:这篇文章是用 literate Haskell 写的。您可以将其保存为 Main.lhs 并在您的 GHCi 中尝试。


首先简短的说明:您可以去掉do. 此外,putStrLn有 type IO (),所以你不需要return ()

interactiveConcat1 = do 
  x <- getLine
  y <- getLine
  putStrLn $ g x y

我们将使用IO,因此导入Control.ApplicativeorControl.Monad会派上用场:

> module Main where
> import Control.Applicative

> -- Repeat your definition for completeness
> g :: [a] -> [a] -> [a]
> g = (++)

你正在寻找这样的东西:

> interactiveConcat :: IO ()
> interactiveConcat = magic g getLine getLine >>= putStrLn

需要什么类型的magic?它返回 a IO String,接受一个返回 an 的函数,String并接受通常String的 s,并接受两个IO Strings:

magic :: (String -> String -> String) -> IO String -> IO String -> IO String

我们大概可以将这种类型推广到

> magic :: (a -> b -> c) -> IO a -> IO b -> IO c

快速的hoogle 搜索显示,已经有两个函数具有几乎这种类型:liftA2fromControl.ApplicativeliftM2from Control.Monad。它们是为每个Applicative和定义的——如果是liftM2—— Monad。由于IO是两者的实例,您可以选择其中一个:

> magic = liftA2

如果你使用 GHC 7.10 或更高版本,你也可以使用<$>and <*>without import 并写interactiveConcat

interactiveConcat = g <$> getLine <*> getLine >>= putStrLn

为了完整起见,让我们添加一个main,以便我们可以通过以下方式轻松检查此功能runhaskell Main.lhs

> main :: IO ()
> main = interactiveConcat

一个简单的检查表明它按预期工作:

$ echo "你好\n世界" | runhaskell Main.lhs
你好世界

参考

于 2015-08-20T10:56:39.550 回答
4

您可以使用liftA2(或liftM2Control.Monad):

import Control.Applicative (liftA2)
liftA2 g getLine getLine >>= putStrLn
于 2015-08-20T10:40:44.527 回答