do
符号允许我们表达monadic代码而不会压倒嵌套,所以
main = getLine >>= \ a ->
getLine >>= \ b ->
putStrLn (a ++ b)
可以表示为
main = do
a <- getLine
b <- getLine
putStrLn (a ++ b)
但是,假设语法允许... #expression ...
代表do { x <- expression; return (... x ...) }
。例如,foo = f a #(b 1) c
将被视为:foo = do { x <- b 1; return (f a x c) }
。然后,上面的代码可以表示为:
main = let a = #getLine in
let b = #getLine in
putStrLn (a ++ b)
哪个会被贬低为:
main = do
x <- getLine
let a = x in
return (do
x' <- getLine
let b = x' in
return (putStrLn (a ++ b)))
这相当于。这种语法对我很有吸引力,因为它似乎提供了与do-notation相同的功能,同时还允许一些较短的表达式,例如:
main = putStrLn (#(getLine) ++ #(getLine))
所以,我想知道这个提议的语法是否存在任何缺陷,或者它是否确实完整且与等号表示相同。
答案 0 :(得分:11)
putStrLn
已经String -> IO ()
,因此您的贬值... return (... return (putStrLn (a ++ b)))
最终会出现类型IO (IO (IO ()))
,这可能不是您想要的:运行此程序赢了打印什么!
更一般地说,你的符号不能表达 [见Derek Elkins&#39 ;评论。] do
- return
中没有结束的块。
我不相信你的符号可以表达join
,可以用do
来表达,而不需要任何其他功能:
join :: Monad m => m (m a) -> m a
join mx = do { x <- mx; x }
但是,您可以将fmap
限制为Monad
:
fmap' :: Monad m => (a -> b) -> m a -> m b
fmap' f mx = f #mx
和>>=
(以及其他所有内容)可以使用fmap'
和join
来表达。因此,添加join
会使您的符号完整,但在许多情况下仍然不方便,因为您最终需要join
的批次。
但是,如果您从翻译中删除return
,则会得到与Idris' bang notation非常相似的内容:
在许多情况下,使用do-notation会使程序不必要地冗长,特别是在上面
m_add
的情况下,立即使用值绑定一次。在这些情况下,我们可以使用速记版本,如下所示:m_add : Maybe Int -> Maybe Int -> Maybe Int m_add x y = pure (!x + !y)
表示法
!expr
表示应该计算表达式expr
然后隐式绑定。从概念上讲,我们可以将!
视为具有以下类型的前缀函数:(!) : m a -> a
但请注意,它不是真正的函数,只是语法!实际上,子表达式
!expr
会在当前范围内尽可能提升expr
,将其绑定到新名称x
,并将!expr
替换为x
}。表达式首先从左到右提升深度。在实践中,! - 注释允许我们以更直接的方式编程,同时仍然给出关于哪些表达式是monadic的符号线索。例如,表达式:
let y = 42 in f !(g !(print y) !x)
解除了:
let y = 42 in do y' <- print y x' <- x g' <- g y' x' f g'
讨论了将其添加到GHC,但拒绝(到目前为止)。不幸的是,我无法找到讨论它的主题。
答案 1 :(得分:2)
这个怎么样:
do a <- something
b <- somethingElse a
somethingFinal a b