fantasyland / sweet-fantasies

sweet.js macros for Fantasy Land compatible structures.
34 stars 7 forks source link

Bugs in do-notation #5

Open raimohanska opened 10 years ago

raimohanska commented 10 years ago

I'd like to use do-notation for Async monad (the thing in my blog). I did some experimenting and found a couple of issues with the do macro.

So firstly, I cannot

$do {
  x <- Async.of("cats")
  y <- Async.of("dogs")
  Async.liftIO(function() {
    console.log(x, y)
  })
}

.. because the macro requires the last expression to be a return.

I'm happy though that this indeed works:

$do {
  x <- Async.of("cats")
  y <- Async.of("dogs")
  return console.log(x, y)
}

The second problem is that multiple returns don't work, so I can only 1 "naked IO" per do-block. See failing test case: 9940a1e791da5eec1f937717af127c63d825a8e9

Also, var bindings in the "tail" do not work as in

$do {
  x <- Async.of("cats")
  y <- Async.of("dogs")
  both = x + y
  return console.log(both)
}

There's a failing test case for this too, at 3fb4326860c413d532c8fc3b88b0ee3433fd464f.

Having said this all, I still want to thank you for all these Sweet Fantasies! I think there's good stuff on the way but it needs some polishing first. Sorry for not having time to start looking for fixes yet.

raimohanska commented 10 years ago

Oh yet another thing. You cannot

$do {
  x <- Async.of("cats")
  y <- Async.of("dogs")
  return console.log("hello", x, "and", y)
}.run()

i.e. put anything on the same line as the macro. That might be a sweet.js feature?

jonifreeman commented 10 years ago

The first one is a known limitation (https://github.com/puffnfresh/sweet-fantasies/blob/master/src/do.sjs#L39). I was unable to make that work with sweet.js 0.1. We should look into that again now that sweet.js is upgraded to 0.2.

'var bindings in the "tail" ' is probably just an oversight and easy to fix. Support for multiple returns may require some larger refactorings on how the macros are currently done.

markandrus commented 10 years ago

One var binding in the tail is an easy fix. Multiple var bindings in the tail is trickier. I think it should be writable as a repeated pattern, but couldn't get it to match.

markandrus commented 10 years ago

Actually, changing the $x:ident = $y:expr rules to be preceded by let $x:ident = $y:expr allows the repeated pattern let $($x:ident = $y:expr) (let) ....

This would allow one or more var-bindings in the tail

$do {
  x <- Async.of(1)
  y <- Async.of(2)
  let plus = x + y
  let minus = x - y
  return console.log(plus * minus)
}

Edit: maybe var instead of let would be more appropriate?

markandrus commented 10 years ago

I believe there are many more cases we haven't accounted for, but I encountered some trickiness supporting if-statements and took a break working on it: https://gist.github.com/markandrus/8795723