Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exception handling examples #36

Closed
junosuarez opened this issue Apr 20, 2013 · 8 comments
Closed

Exception handling examples #36

junosuarez opened this issue Apr 20, 2013 · 8 comments

Comments

@junosuarez
Copy link

I was reading through http://book.realworldhaskell.org/read/error-handling.html and trying to see how it would be adapted to Fantasy Land js, but struggling a little bit. I would really appreciate if anyone could come up with some example code to deal with this situation:

Assume some Fantasy Land compliant promises implementation:

var err = Promise.reject(new Error('broken'))
var ok = Promise.of(10)

var sum = liftA2(err, ok, function (a, b) {
  // this should never be called
  return a + b
})
return sum

// later, in some other scope, where I only have access to sum:

sum.chain(function (sum) {
  // this should also never be called
})

// how do I get the error message?
@Raynos
Copy link
Contributor

Raynos commented Apr 20, 2013

Error is just an instance of Either. Promise<Either<Error, Value>>

Now that probably won't do what you want because you will have an Either to deal with in every transformation. What you probably want is some form of Continuable<Value> abstraction that's both a functor & monad where the behaviour is simple

function map(lambda) { return function (source) {
  return function continuable(callback) {
    source(function (err, value) {
      return err ? callback(err) : callback(null, lambda(value))
    }
  }
} }

Note what we are doing here is saying that an async data type is a boxed thing that is either an Error and a Value and we have defined a pure & lazy map that will transform the value if it exists and not handle the error.

This means when you use it like

ajax(...).map(JSON.parse)(function (err, json) {
  // handle err here if exists else json
})

You can handle the error there and then.

TL:DR; All values inside a boxed asynchronous thing are of type Either<Error, Value>. You just implement map & chain to do a no-op pass through for Error and then at the end of your chained map and chain you can actually read the value. This strategy only works if map and chain are lazy.

The specific answer to // how do I get the error message? is that you need a method that's not map / chain / liftA2 that basically reads or consumes the thing with the desire to have some form of side effect. Because whenever you read a value from an async source your eventually going to do a side effect with it like render it on the screen.

@Raynos
Copy link
Contributor

Raynos commented Apr 20, 2013

It should be noted that in promise land the function for consuming a promise is called .done() I think.

@Twisol
Copy link

Twisol commented Apr 20, 2013

@Raynos: I'm not really sure where all of that is coming from. Most (all?) promise implementations I've seen conflate the Either and Future together into a single type, so you never deal with an Either directly. I'd certainly prefer to see some kind of Promise err a = FutureT (Either err a), but it doesn't reflect actual usage in Javascript.

Also, all monads are automatically functors. First I've heard of a done method, too.

@jden: The error part of a Promise isn't exposed by the Monad interface. Monads only explicitly deal with the success path, though a correct Monad instance would necessarily have to propagate the error internally. Most promise libraries provide some kind of otherwise or onRejected function (related to the MonadError typeclass in Haskell), which you can use to tap into the error path of your computation. In this way, otherwise is just chain over the error path.

It's also been shown that if you only have separate chain and otherwise methods, you lose some of the power provided by a function that accepts both success and error handlers at once. This method is specified by Promises/A+ as then, although some of then's current behavior has been under intense debate.

@Raynos
Copy link
Contributor

Raynos commented Apr 20, 2013

@Twisol promises-aplus/unhandled-rejections-spec#5

If you were implementing lazy promises the invocation of done is where you would actually start doing side effects.

Promises do conflate Either and Future together but that's basically how they work under the hood. Does haskell provide higher order functions that play nice with Monad (Either err a) ?

@Twisol
Copy link

Twisol commented Apr 20, 2013

@Raynos:

If you were implementing lazy promises the invocation of done is where you would actually start doing side effects.

I see. Lazy promises are different from non-lazy promises however, and if we're just talking about promises in general, you can't rely on the existence of done.

Promises do conflate Either and Future together but that's basically how they work under the hood. Does haskell provide higher order functions that play nice with Monad (Either err a) ?

The key phrase here is "under the hood" - no promise library I know of exposes the internal Either representation. I know for a fact that when.js has an ad-hoc Either in its source code, but it's an explicit goal to make sure that never leaks out to the user. The exposed API wraps/unwraps both parts at once.

I don't understand what Monad (Either err a) means. Either err is a monad; you don't need to supply the Monad part unless you're abstracting over all monads, not just Either. But if I were to guess, you might want to look at MonadError, which provides an additional typeclass for monads that support an error path.

EDIT: Fixed MonadError link.

@Raynos
Copy link
Contributor

Raynos commented Apr 20, 2013

@Twisol I mean a type Monad<Either<Error, Any>> i.e. Future<Either<Error, Any>>, Stream<Either<Error, Any>>, Foldable<Either<Error, Any>>

Basically if I have a monad / applicative / functor that contains Either<Error, Any> are there higher order functions that can deal with it nicely.

For example

function fmapEither(functor, lambda) {
  return functor.map(function (box) {
    return box.left ? box.left : lambda(box.right)
  })
}

This function works on all Functor<Either<Error, Any>> and it should be easy to make Future, Stream & Foldable a functor.

@Twisol
Copy link

Twisol commented Apr 20, 2013

I still have the "colorless green ideas sleep furiously" problem with your syntax, but I see what you mean. Functors can be trivially composed, but monads cannot. One way to do this between specific pairs of monads is to use monad transformers. Unfortunately, that requires a bit more help from the type system to do generically than Javascript provides.

As far as functors go, in Haskell one might trivially use fmap . fmap (meaning the composition of fmap with itself) to apply a function two levels deep. In Javascript (and in particular, the this-dependent approach adopted by Fantasy Land) it isn't as easy. Nevertheless, with some minor additions one can get close:

var compose = function(f, g) {
  return function(x) {
    return f(g(x));
  };
});

var lift = function(f) {
  return function(x) {
    return x.map(f);
  });
};

var lift2 = compose(lift, lift);

I reframed map above as lift to make it easier to compose. compose could also be trivially extended to accept and compose any number of arguments.

@philipnilsson
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants