salsita / prism

React / Redux action composition made simple http://salsita.github.io/prism/
496 stars 24 forks source link

Version 1.x #5

Closed tomkis closed 8 years ago

tomkis commented 8 years ago

As many of you have already noticed, there's a next branch which is currently under heavy development.

There're obviously many options we could go (especially for side effects model):

After many versions we've figured out by trial and error that we want to go the Generators way. We found redux-loop API a way too awkward for use and there's an alternative repo https://github.com/jarvisaoieong/redux-architecture which is using that.

Using Sagas for side effects is nice alternative but composing Sagas is very difficult and there's no silver bullet implementation which would cover all the use cases, therefore Saga composition would have to be done in user land, which is frankly un-acceptable given the fact how difficult it is. Also sagas would ideally be un-aware of app state which discriminates Sagas' usefulness.

Using Generators for side effects definitely has its drawbacks:

  1. All the function that takes callbacks must be generator friendly
  2. You have to explicitly yield* effects when composing them (which some people consider advantage because it's explicit)

We've decided to use Generators because of two reasons:

  1. We are already using that approach in production on pretty complex applications and it simply works
  2. Generators seemed like the least pain from all the options above.

    Goals for 1.x

next branch now serves as development branch for 1.x, you can even install it using npm:

npm install redux-elm@1.x-latest

Lightweight

We believe that modern application architecture should be based on solid Patterns, not thousand lines of Magic code therefore we'd like to keep redux-elm as lightweight as possible.

Updater Abstraction and Side Effects

The idea is that we will provide Updater abstraction which can be converted into Generator friendly Reducer (we assume that https://github.com/salsita/redux-side-effects is being used).

Pattern matching

Pattern Matching is essential part of this framework and we decided to give User an interface to provide any pattern matching implementation they need. We also provide 3 basic matchers which will serve in 90% of use cases.

Great documentation

Because redux-elm is about patterns, not implementation we are also working on very thorough documentation which should be great starting point for people who are not even experienced with redux or Elm.

Stay tuned!

tomkis commented 8 years ago

Please, we are still open to ideas / suggestions therefore feel free to use this issue.

tomkis commented 8 years ago

Next has been merged to master and 1.3.1 has been released.

jmatsushita commented 8 years ago

Have you considered async/await for the side-effects?

eliperelman commented 8 years ago

Async/await cannot handle all the aspects of the side effects and would make testing much more difficult, as has been laid out in redux-saga:

https://github.com/yelouafi/redux-saga/issues/7 http://yelouafi.github.io/redux-saga/docs/basics/DeclarativeEffects.html

jmatsushita commented 8 years ago

Ah ha I see. But couldn't we use async/await instead of Promises (typically in effects.js) while keeping sagas for their declarative awesomeness?

eliperelman commented 8 years ago

Yes, that is entirely possible, but I'm not sure there is much benefit. redux-saga's call function is already Promise-aware:

// effects.js

// this function returns a promise
export const fetchRandomGif = () => fetch('/gif/random');
import { call } from 'redux-saga/effects';
import { fetchRandomGif } from './effects';

const response = call(fetchRandomGif);
import { fetchRandomGif } from './effects';

const response = await fetchRandomGif();

So as I see it, there is not much to be gained.

eliperelman commented 8 years ago

Also async/await (Async Functions) are mostly sugar on top of Promises. You can't use them without a Promise being created somewhere along the line.

tomkis commented 8 years ago

So let me explain why async/await can't be used for side-effects.

First of all, when you talk about side effects, you need to be more specific what you exactly mean by side effects, since you have mentioned async/await I suspect that you meant API call, however there might potentially be side effects which are not asynchronous, for example logging. When you call console.log it's impurity yet it's totally synchronous operation and would not benefit from async functions at all.

However, I am pretty much convinced that when you mentioned side effects, you actually meant long lived transactions. It's really confusing because even redux-saga claims that it is:

An alternative side effect model for Redux apps

Which frankly, is a nonsense, because redux-saga has nothing to do with side effects. Saga is pattern for Long Lived Transaction (LLT), which in Redux context means series of actions (which may or may not be asynchronous). Typically API call is LLT because it takes more than one action (Request, Response) in place and it lives in time (asynchronous).

async/await is just a syntax sugar on top of Promises, you can just wait for Promise resolving, that's nice, it could help us with our Request <-> Response scenario, but what if our series of actions does not contain only two actions (Request, Response) but may be potentially infinitely long series? It's still transaction, right? It's still long lived, right?

async/await helps you when you need to asynchronously wait for single value. Saga on the other hand can asynchronously wait for many values.

In other words the most trivial Saga definition is something which can accept infinite number of actions and dispatch infinite number of new actions, although it's not just map it's more of flatMap because reaction to one action, may potentially dispatch more than just one action.

And of course, there's also really nice abstraction and it's RxJS Subject ;-) (coming soon)