没有 do 块或累积括号的惯用 Haskell 语法?
Idiomatic Haskell syntax without do-blocks or accumulating parentheses?
我是 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?我希望问什么是 "idiomatic"?
不会太主观
此外,如果有任何使 emacs 比 (Haskell Ind) 模式更好协作的技巧,我们将不胜感激。谢谢!
约翰
编辑:我偶然发现 https://wiki.haskell.org/Do_notation_considered_harmful 并意识到我编写的 lambda 链中的嵌套括号不是必需的。然而,社区(和 ghc 实现者)似乎已经接受了使用 、<*> 等的 Applicative 风格,这似乎使代码更易于阅读,尽管弄清楚运算符优先级令人头疼。
您可以使用 liftA2
(或 Control.Monad
中的 liftM2
):
import Control.Applicative (liftA2)
liftA2 g getLine getLine >>= putStrLn
注:这个post是写文的Haskell。您可以将其保存为 Main.lhs 并在您的 GHCi 中尝试。
首先简短说明:您可以去掉 do
中的分号和大括号。此外,putStrLn
具有类型 IO ()
,因此您不需要 return ()
:
interactiveConcat1 = do
x <- getLine
y <- getLine
putStrLn $ g x y
我们将使用 IO
,因此导入 Control.Applicative
或 Control.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
需要什么类型?它 returns 一个 IO String
,接受一个 returns 一个 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
来自 Control.Monad
的 quick hoogle search reveals that there are already two functions with almost that type: liftA2
from Control.Applicative
and liftM2
。它们是为每个 Applicative
和 – 在 liftM2
– Monad
的情况下定义的。由于 IO
是两者的实例,您可以选择其中之一:
> magic = liftA2
如果你使用GHC 7.10或更高版本,你也可以使用<$>
和<*>
而不导入,并将interactiveConcat
写成
interactiveConcat = g <$> getLine <*> getLine >>= putStrLn
为了完整性,让我们添加一个 main
以便我们可以通过 runhaskell Main.lhs
:
轻松检查此功能
> main :: IO ()
> main = interactiveConcat
一个简单的检查表明它按预期工作:
$ echo "Hello\nWorld" | runhaskell Main.lhs
HelloWorld
参考资料
Applicative
在 Typeclassopedia
- LYAH's chapter "For a Few Monads More" 的第 "Some useful monadic functions" 部分。
我是 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?我希望问什么是 "idiomatic"?
不会太主观此外,如果有任何使 emacs 比 (Haskell Ind) 模式更好协作的技巧,我们将不胜感激。谢谢!
约翰
编辑:我偶然发现 https://wiki.haskell.org/Do_notation_considered_harmful 并意识到我编写的 lambda 链中的嵌套括号不是必需的。然而,社区(和 ghc 实现者)似乎已经接受了使用 、<*> 等的 Applicative 风格,这似乎使代码更易于阅读,尽管弄清楚运算符优先级令人头疼。
您可以使用 liftA2
(或 Control.Monad
中的 liftM2
):
import Control.Applicative (liftA2)
liftA2 g getLine getLine >>= putStrLn
注:这个post是写文的Haskell。您可以将其保存为 Main.lhs 并在您的 GHCi 中尝试。
首先简短说明:您可以去掉 do
中的分号和大括号。此外,putStrLn
具有类型 IO ()
,因此您不需要 return ()
:
interactiveConcat1 = do
x <- getLine
y <- getLine
putStrLn $ g x y
我们将使用 IO
,因此导入 Control.Applicative
或 Control.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
需要什么类型?它 returns 一个 IO String
,接受一个 returns 一个 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
来自 Control.Monad
的 quick hoogle search reveals that there are already two functions with almost that type: liftA2
from Control.Applicative
and liftM2
。它们是为每个 Applicative
和 – 在 liftM2
– Monad
的情况下定义的。由于 IO
是两者的实例,您可以选择其中之一:
> magic = liftA2
如果你使用GHC 7.10或更高版本,你也可以使用<$>
和<*>
而不导入,并将interactiveConcat
写成
interactiveConcat = g <$> getLine <*> getLine >>= putStrLn
为了完整性,让我们添加一个 main
以便我们可以通过 runhaskell Main.lhs
:
> main :: IO ()
> main = interactiveConcat
一个简单的检查表明它按预期工作:
$ echo "Hello\nWorld" | runhaskell Main.lhs HelloWorld
参考资料
Applicative
在 Typeclassopedia- LYAH's chapter "For a Few Monads More" 的第 "Some useful monadic functions" 部分。