这种语法是否与表示法一样具有表现力?

时间:2017-04-16 04:42:39

标签: haskell syntax do-notation

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))

所以,我想知道这个提议的语法是否存在任何缺陷,或者它是否确实完整且与等号表示相同。

2 个答案:

答案 0 :(得分:11)

putStrLn已经String -> IO (),因此您的贬值... return (... return (putStrLn (a ++ b)))最终会出现类型IO (IO (IO ())),这可能不是您想要的:运行此程序赢了打印什么!

更一般地说,你的符号不能表达do - return中没有结束的块。 [见Derek Elkins&#39 ;评论。]

我不相信你的符号可以表达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