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.
source to share
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 [Cm. comment by Derek Elkins.]do
-block that does not end with return
.
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 expressionexpr
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 raiseexpr
as high as possible within the current scope, bind it to a new name,x
and replace it!expr
withx
. 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.
source to share