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

Theory: Task is good not only for async, but also for composing sync side effects in a pure fashion #8

Open
rpominov opened this issue Jul 29, 2016 · 5 comments

Comments

@rpominov
Copy link
Owner

Just an area I'm going to explore. Nothing in particular to write about just yet.

@dskelton-r7
Copy link

Could you not use IO https://github.com/fantasyland/fantasy-io for a synchronous way to perform effects?

@rpominov
Copy link
Owner Author

rpominov commented Aug 2, 2016

Yea, IO is the monad that usually used for this. And IO can be used for async as well I think. So I was thinking maybe Task could also be a universal monad for async/impure stuff. In this case we could write pure programs in JS expressed as Tasks compositions. We would need to wrap all impure APIs in Task based APIs for this.

Currently I don't know whether this will work and whether this is a good idea. I'm going to try to build some examples.

@Avaq
Copy link

Avaq commented Aug 3, 2016

I've been using Futures to this effect, in combination with Future.encase if I need synchronous error handling.

The advantages of this approach, I find, are:

  • Futures are at hand. I was already using them, so it's easy to use them to this end as well.
  • Futures have built-in error handling (as opposed to IO).
  • My types are easier to line up (no need to convert IO to a Future).

However, I believe there is a severe disadvantage, which is something I have been thinking about lately. One benefit of using monads, is that you are explicit about effects. If you unwrap a structure of Async IO Either, you know that

  1. It's asynchronous
  2. It performs a side-effect
  3. It can fail

In the case of Futures/Tasks, all of this explicit information is lost. Futures are essentially Async, IO and Either all-together. The problem here is that if you unwrap a Future, you don't know which effects are going to happen:

  1. Is it asynchronous?
  2. Maybe it performs a side effect too?
  3. Can it fail? Or is it just a safe asynchronous operation?

Though in practice this is not often a problem, I did run into a situation where I had to maintain a
Future Error (Future a b) structure throughout a section of my code, because the inner effect had to happen after the outer effect. Essentially my program would break if I flat mapped the structure, which is not supposed to happen if it's the same type.

@rpominov
Copy link
Owner Author

rpominov commented Aug 7, 2016

Can it fail? Or is it just a safe asynchronous operation?

Flow can help with this one.

// empty type, we can't create a value of this type
type Empty = void & null

const myTask: Task<string, Empty> = Task.create((succ, fail) => {
  // can't call fail() because can't create a value that could be passed to it
})

Also Flow can show type of any variable (with a good editor plugin), so if we see that some variable is of type Task<string, Empty> we know that it's a Task that can't fail.

See in action: https://github.com/rpominov/fun-task/blob/master/examples/io/1.js


Update: empty type might be added to Flow facebook/flow#2225

@rpominov rpominov removed the raw idea label Oct 3, 2016
@rpominov
Copy link
Owner Author

rpominov commented Oct 17, 2016

Another example based on this idea: https://jsfiddle.net/eh93ry0o/9/

Update: for the record couple more examples and thoughts are in this twitter thread https://twitter.com/rpominov/status/788853797175042049

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

3 participants