Closed faceyspacey closed 8 years ago
There are two benefits: 1) Elm provides type safety so you'll get much more compiler information when something doesn't work - it works especially well with the Elm Architecture 2) There are no runtime errors in Elm
Generators are not necessary unless you need side effects and even simple applications need side effects, for example API communication, logging, caching etc. https://github.com/salsita/redux-side-effects - This package allows us to reduce side effects using generators... Basically any function yielded
within reducer is not immediately executed, instead its execution is deferred and dispatch
is provided -> this is basically same approach which Elm is using, but Elm is using declarative effects which are reduced as well, therefore Elm's updater returns Pair<Model, Effects>
and we emulate the same by using yield
for emitting thunks (side effects) and the returned value is just plain old mutated Model.
@faceyspacey maybe you'll like olmo though I need to port all the examples
To achieve a variation of benefit 1), in our project, we use excelent tcomb library, similar to how it is done in tcomb-redux. It follows composition of state and actions nicely. @tomkis1, I can do a PR here, if you are interested to see how it fits with examples.
We started the project about two months ago, and came up with almost identical redux aditions as you implemented here (forwardTo, mapEffects), which is no coincidence since we also wanted to port critical parts of Elm architecture to Redux and are using your redux-side-effects. :) So far we are very satisfied how it grows around such architecture.
@minedeljkovic I am really glad that we are sharing the view.
To achieve a variation of benefit 1), in our project, we use excelent tcomb library, similar to how it is done in tcomb-redux. It follows composition of state and actions nicely. @tomkis1, I can do a PR here, if you are interested to see how it fits with examples.
I was something about something less intrusive Flow? TypeScript? Yet I am more inclined to Flow as it's babel friendly.
We started the project about two months ago, and came up with almost identical redux aditions as you implemented here (forwardTo, mapEffects), which is no coincidence since we also wanted to port critical parts of Elm architecture to Redux and are using your redux-side-effects. :) So far we are very satisfied how it grows around such architecture.
Frankly, we are getting more and more skeptical about Generators as using them has its drawbacks. You can't use callbacks because yield*
is not automatically propagated which is a showstopper for example when using something like immutable.js.
So my long term idea is think about Elmish composability of sagas using https://github.com/salsita/redux-saga-rxjs and avoid side effects in reducers at all. Also, I would rather avoid nesting actions. Instead, the composition would be done via action naming so that we can do easy pattern matching. Action would be something like
{
type: 'COUNTERS.TOP.INCREMENT',
payload: 1
}
@tomkis1 could you explain why pattern matching on the action name is better than nesting actions in your opinion? I wonder if there is a difference in performance also when you want to check for a "match".
@tomsdev it's possible to use regexp for pattern matching with plain old strings so it allows us more advanced matching. Also, unwrapping is easy because there's no need to recursively access the action.payload.payload.payload...
@tomkis1 in which way do you think immutable.js is incompatible with redux-side-effects? can you give an example of a situation in which i need to yield inside a immutable.js callback.
@namjul imagine following:
function* updater(immutableAppState) {
return immutableAppState.map(item => {
yield ApiSideEffect();
return item.set('loading', true);
});
}
This simply doesn't work and it doesn't work with anonymous generator as well.
function* updater(immutableAppState) {
return yield* immutableAppState.map(function*(item) {
yield ApiSideEffect();
return item.set('loading', true);
});
}
does not work either.
right thx. but wouldn't the following work:
function* item(item) {
yield ApiSideEffect();
return item.set('loading', true);
}
function* update(immutableAppState = Immutable.Map(), action) {
return immutableAppState.set(action.id, yield* item(state.get(action.id), action))
}
probably there are still situations you need an yield directly in the map callback.
In your next branch i saw that you couple redux-elm with redux-side-effects. What i would like is to use your redux-elm action-pattern-routing solution + redux-loop to solve the problem with immutable.js. Is there a way we can use these seperatly?
so i tried it and build the randomGifList with immutable.js without using map:
.case(name, function*(state, action, randomGifId) {
return state.setIn(['gifList', randomGifId], yield* mapEffects(randomGif.reducer(state.getIn(['gifList', randomGifId]), action), name, randomGifId))
}, Matchers.parameterizedMatcher)
@namjul
but wouldn't the following work:
function* item(item) {
yield ApiSideEffect();
return item.set('loading', true);
}
function* update(immutableAppState = Immutable.Map(), action) {
return immutableAppState.set(action.id, yield* item(state.get(action.id), action))
}
It would not, because your item
function is generator and calling generator returns iterable therefore it would only set iterable into the App state.
probably there are still situations you need an yield directly in the map callback.
There's (or should be) a workaround by allowing yielding
array of effects, that way you could map effects using traditional map
function and then yield
the result.
In your next branch i saw that you couple redux-elm with redux-side-effects. What i would like is to use your redux-elm action-pattern-routing solution + redux-loop to solve the problem with immutable.js. Is there a way we can use these seperatly?
For now, it looks like we will lock this repo with Generators because redux-loop
seemed too awkward for us to use, there's an alternative project which is doing that though https://github.com/jarvisaoieong/redux-architecture
so i tried it and build the randomGifList with immutable.js without using map:
Yes that's definitely a solution, however you have to realize that with redux-elm
you will most like won't even need immutable js because entire application state is nicely sliced into smaller chunks and spread
operator serves pretty well here.
yes, i can see that immutable.js is likely not needed with redux-elm. i am still confused why the first example would not work. Its similar to the second example and it works. yield* would just return the return statement from the item generator function. Which would be an immutable.js object...
function* anotherGenerator(i) {
yield i + 1;
yield i + 2;
yield i + 3;
return 2;
}
function* generator(i){
yield i;
console.log(yield* anotherGenerator(i));
yield i + 10;
}
var gen = generator(10);
console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
// 2
console.log(gen.next().value); // 20
it just returns the return value not an iterable. i probably miss something.
So my long term idea is think about Elmish composability of sagas using https://github.com/salsita/redux-saga-rxjs and avoid side effects in reducers at all.
to you mind to show me how this would look like? if i define the fetchGif effect in randomGif module how can i reuse/compose it in randomGifList with redux-saga-rxjs. or is this ATM not possible.
it just returns the return value not an iterable. i probably miss something.
No you are not, sorry my bad, I overlooked the yield*
keyword. You'r right that will work.
to you mind to show me how this would look like? if i define the fetchGif effect in randomGif module how can i reuse/compose it in randomGifList with redux-saga-rxjs. or is this ATM not possible.
You can actually check history of the repo https://github.com/salsita/redux-elm/blob/v0.3.0/examples/src/6-list-of-random-gif-viewers/main.js#L58 the composeSaga
function does the trick, though the implementation is wrong and does not work for all use cases.
Saga composition is indeed really hard to achieve but doable. Anyway, we've decided to go the Generators way, at least for 1.x
Closing this issue in favour of https://github.com/salsita/redux-elm/issues/5
...i really like it by the way? ..Also why are generators needed?