Is this syntax as expressive as do-notation?

Notation do

allows us to express monadic code without overwhelming nesting, so

main = getLine >>= \ a -> 
       getLine >>= \ b ->
       putStrLn (a ++ b)

      

can be expressed as

main = do
  a <- getLine
  b <- getLine
  putStrLn (a ++ b)

      

Suppose the syntax allows you to ... #expression ...

stand for do { x <- expression; return (... x ...) }

. For example, foo = f a #(b 1) c

it will desugared as: foo = do { x <- b 1; return (f a x c) }

. The above code could be expressed like this:

main = let a = #getLine in
       let b = #getLine in
       putStrLn (a ++ b)

      

Which will be desugared as:

main = do
  x <- getLine
  let a = x in
    return (do
      x' <- getLine
      let b = x' in
        return (putStrLn (a ++ b)))

      

This is equivalent. This syntax is appealing to me because it seems to offer the same functionality as do-notation, and also allows for shorter expressions such as:

main = putStrLn (#(getLine) ++ #(getLine))

      

So, I am wondering if there is something flawed with this suggested syntax or if it is indeed complete and equivalent to do-notation.

+3


source to share


2 answers


putStrLn

already String -> IO ()

, so your desugaring ... return (... return (putStrLn (a ++ b)))

ends up having a type IO (IO (IO ()))

, which is most likely not what you wanted: running that program won't print anything!

More broadly, your notation cannot express any do

-block that does not end with return

.
[Cm. comment by Derek Elkins.]

I don't believe your notation can express join

, which can be expressed with do

without any additional functionality:

join :: Monad m => m (m a) -> m a
join mx = do { x <- mx; x }

      

However, you can express fmap

with constraints on Monad

:



fmap' :: Monad m => (a -> b) -> m a -> m b
fmap' f mx = f #mx

      

and >>=

(and therefore everything else) can be expressed with fmap'

and join

. So adding join

will make your entries complete, but still awkward in many cases because you need a lot of join

s.

However, if you drop return

from translation, you end up with something very similar to Idris Identity :

In many cases, using do-notation can make programs unnecessarily verbose, especially in cases like the one m_add

above where the value boundary is used once, immediately. In these cases, we can use a shorthand version:

m_add : Maybe Int -> Maybe Int -> Maybe Int
m_add x y = pure (!x + !y)

      

The notation !expr

means that the expression expr

should be evaluated and then implicitly linked. Conceptually, we can think of !

as a prefix function with the following type:

(!) : m a -> a

      

Note, however, that this is not a function, but just syntax! In practice, the subexpression !expr

will raise expr

as high as possible within the current scope, bind it to a new name, x

and replace it !expr

with x

. The expressions go up first, from left to right. On practice! -notation allows us to program in a more straightforward way, yet still gives us a notation to indicate which expressions are monadic.

For example, the expression:

let y = 42 in f !(g !(print y) !x)

      

rises to:

let y = 42 in do y' <- print y
                 x' <- x
                 g' <- g y' x'
                 f g'

      

An addition to the GHC has been added but has been rejected (for now). Unfortunately I cannot find threads discussing this.

+11


source


How about this:



do a <- something
   b <- somethingElse a
   somethingFinal a b

      

+2


source







All Articles