习惯的Haskell语法,没有do-block或累加括号



我对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污染盒中剥离值?也许有一个更干净的方式使用升降机*或fmap?我希望问什么是"习惯用语"不会太主观。

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

约翰

编辑:我偶然发现https://wiki.haskell.org/Do_notation_considered_harmful并意识到我写的lambda链中的嵌套括号是不必要的。然而,社区(和ghc实现者)似乎已经接受了应用程序启发的风格,使用<*>等,这似乎使代码更容易阅读,尽管计算操作符优先级令人头痛。

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


先说一句:你可以去掉do中的分号和大括号。此外,putStrLn具有类型IO (),因此您不需要return ():

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

我们将使用IO,因此导入Control.ApplicativeControl.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需要什么类型?它返回一个IO String,接受一个函数,该函数返回一个String,接受一个通常的String s,并接受两个IO String s:

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

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

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

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

> magic = liftA2

如果您使用GHC 7.10或更高版本,您也可以使用<$><*>而不需要导入,并将interactiveConcat写入

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

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

> main :: IO ()
> main = interactiveConcat

一个简单的检查显示它按预期工作:

$ echo "HellonWorld" | runhaskell Main.lhsHelloWorld 之前引用

  • Applicative在分类目录
  • LYAH的"For a Few Monads More"一章中的"一些有用的单元函数"一节。

您可以使用liftA2(或Control.Monad中的liftM2):

import Control.Applicative (liftA2)
liftA2 g getLine getLine >>= putStrLn

最新更新