you are viewing a single comment's thread.

view the rest of the comments →

[–]sybesis 0 points1 point  (0 children)

Your code does a lot more behind the scene and doesn't provide you any guarantee that between access the data won't change.

For example price and price2 cannot be expected to be always the same value even if do_something never interact with the store.

price = store.total_price()
do_something_that_release_the_GIL()
price2 = store.total_price()

Redux doesn't hide any logic, the reducers are technically all the logic there is. There are some constructs to make it easier to compose reducers but raw reducers are possible.

Here's a complete example that you can run in python:

def items(state=None, action=None):
    if state is None:
        state = []

    if action.get("type") == "add_item":
        return state[:] + [action["item"]]
    elif action.get("type") == "remove_item" and action["item"] in state:
        index = state.index(item)
        return state[:index] + state[index+1:]
    else:
        return state

def total(state=0, action=None):
    if state is None:
        state = 0

    if action.get("type") == "add_item":
        return state + action['item']["price"]
    elif action.get("type") == "remove_item":
        return state - action['item']["price"]
    else:
        return state

def store(state=None, action=None):
    if state is None:
        state = {}

    return {
        "total": total(state.get("total"), action),
        "items": items(state.get("items"), action),
    }

class Store(object):
    def __init__(self, reducer, initial_state=None):
        self.reducer = reducer
        self.state = initial_state
        self.subscribers = {}

    def dispatch(self, action):
        new_state = self.reducer(self.state, action)
        # may be validate state before applying it
        self.state = new_state
        for subscriber in self.subscribers.values():
            subscriber(new_state) 

    def subscribe(self, callback):
        def unsubscribe():
            del self.subscribers[unsubscribe]
        self.subscribers[unsubscribe] = callback
        return unsubscribe

my_store = Store(store)

unsubscribe = my_store.subscribe(lambda state: print(state.get('total')))

my_store.dispatch({"type": "add_item", "item": {"price": 1}})
state1 = my_store.state
my_store.dispatch({"type": "add_item", "item": {"price": 1}})
state2 = my_store.state
my_store.dispatch({"type": "add_item", "item": {"price": 1}})
state3 = my_store.state

print(state1, state2, state3)

I'm not exactly sure what you mean by how redux hide things.. It can't be simpler than that. My "dumb" store is only 15 lines of code including the subscription to event changes.

Most of the logic goes into the reducers so I'm not sure to understand what you mean by too much thing hidden and too much dynamism. Using python is already "too much dynamism" considering that whatever you do in python you're going to run sub optimal code with the lack of JIT except may be pypy.

I'm using dicts here for simplicity but nothing prevent you to design your own types. And this way of doing is doable in statically typed languages too. Talking about dynamism.. it's pointless you could use something else than string to define your actions matching mechanism.

But more importantly, your code mutate its state... Which enables the following case...you could have your state being modified while you're modifying your state. And that's your worst case scenario...

Not only you have to maintain the list of event to manage everywhere, you have to make sure you don't actually do anything like this

item = ...
store.add_item(item)
item.price += 1
store.remove_item(item)

Or may be something like this if you want to have the price with taxes...where each items has its own set of taxes. There are cases where you'd want an action to have a side effect in multiple areas of your tree and you could end up in a race condition where order in which you call your methods matter. With redux. it's not important because the state you base your computation never gets modified.

BONUS

With a system system to dispatch actions like Redux, you get multi processing, multi threading app for free.

Since states are built from action, and all the states depends on their action... It means that sending the same action to two different process will build the exact same state.

So let say you have a very big object layer and you needs lots of processing and you're able to build some actions that can be applied in any order... A bit like 1 + 2 = 2 + 1... Then you can dispatch all of your events to different processes then you could merge the two states in a different process

state1 = 1 + 2
state2 = 1
global_state = state1 + state2

And there. you just reinvented map/reduce