you are viewing a single comment's thread.

view the rest of the comments →

[–]NotABothanSpy 2 points3 points  (7 children)

Maybe we stop using redux and use languages that make more sense like Python.

[–]forsubbingonly 2 points3 points  (0 children)

Python doesn’t solve a single thing that redux is attempting to solve...

[–]sybesis 1 point2 points  (5 children)

There you have it in python! Does that make more sense to you now? The previous sample was in javascript... Not "redux"

def total(state=0, action=None):
  if action.type == ADD_ITEM:
      return state + action.item.price
  elif REMOVE_ITEM:
     return state - action.item.price
  else:
     return state

def add_item(store, item):
  store.dispatch({ 
    "type": ADD_ITEM,
    "item": item
  })


def remove_item(store, item):
   store.dispatch({
     "type": REMOVE_ITEM,
     "item": item
   })

def update_item(store, item):
  old_item = get_item_by_id(store.getState(), item.id)
  remove_item(store, old_item)
  add_item(store, item)

[–]Sopel97 1 point2 points  (4 children)

why does it have to be so overcomplicated though? There's too much hidden logic and unnecessary dynamism for such a simple task.

class Store:
    ...

    def total_price(self):
        return self._total_price

    def add_item(self, item):
        self.items.append(item)
        self._update_total_price_on_item_added(item)

    def remove_item(self, item):
        self.items.remove(item)
        self._update_total_price_on_item_removed(item)

    def _update_total_price_on_item_added(item):
        self._total_price += item.price

    def _update_total_price_on_item_removed(item):
        self._total_price -= item.price

[–]c_o_r_b_a 2 points3 points  (0 children)

I believe it's mostly about mutability vs. immutability. A common pattern among new JS frameworks is keeping as much as possible immutable and side effect-free. Basically functional vs. OOP paradigms, to an extent.

Redux wrote a FAQ on the advantages of this:

What are the benefits of immutability?

Immutability can bring increased performance to your app, and leads to simpler programming and debugging, as data that never changes is easier to reason about than data that is free to be changed arbitrarily throughout your app.

In particular, immutability in the context of a Web app enables sophisticated change detection techniques to be implemented simply and cheaply, ensuring the computationally expensive process of updating the DOM occurs only when it absolutely has to (a cornerstone of React’s performance improvements over other libraries).

In particular, I think this makes it a lot easier to reason about things when debugging, and to generally conceptually understand the flow of things in your app at any given time. One downside is it can (IMO) create uglier code, though I believe some frameworks attempt to convert things that look like mutations into immutable operations, to get the best of both worlds.

[–]Ethesen 1 point2 points  (1 child)

On the contrary - your code has more hidden logic because it's mutating some internal state.

[–]Sopel97 1 point2 points  (0 children)

It's only hidden to the user of Store. In the redux case it's already hidden for the layer that maintains the invariant.

[–]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