foxdonut / meiosis

meiosis
https://meiosis.js.org
MIT License
415 stars 18 forks source link

Is the view not aware of streams? #5

Closed dmitriz closed 7 years ago

dmitriz commented 7 years ago

Love the introduction here https://github.com/foxdonut/meiosis/wiki/The-Fundamental-Setup but found this confusing:

There is just one source stream: update. The view code does not create additional streams. In fact, the view code is not aware of streams at all; views just call the update as a function that was passed as a callback.

When view is called, it is passed the update stream instead of function:

const update = flyd.stream();
   ...
models.map(model => ReactDOM.render(view(model, update), element));

The only reason it works here is because of the flyd api treating streams as functions. But you need to know this in order to write the view correctly, and it would not work when you replace flyd by another stream library with different api. E.g. if update(val) were replaced by update.emit(val), then the view must be aware of it.

What seem conceptually happen here is some sort of lifting where the update was previously defined for functions, but when called, is actually lifted to stream values.

dmitriz commented 7 years ago

I should add, because stream is passed in place of function update, I find this a bit difficult to unravel:

  const increase = () => update(model => {
    model.value = model.value + 1;
    return model;

Here we are passing the anonymous function to the update stream? The way I understand the flyd api, is that you can pass directly the value:

    update(model.value + 1)

Would it do the same? Any reason to pass the function instead?

Right now the anonymous function is mutating the model object passed to it as argument, so it is not pure. Is it intentional?

foxdonut commented 7 years ago

Hi @dmitriz

Please accept my apologies for the delay in responding! This was not on purpose. I did not get any notification from github and so was not aware that you had opened this issue. I have gotten notifications in the past, so was counting on them instead of manually monitoring the issues tab.

You are correct, views are not aware that they are given streams as callback functions. This is on purpose, so that views are not coupled to implementation details.

As you said, flyd happens to have an api where a stream can be called as a function. But, if you replaced flyd with another stream library, you would only have to make one change at the top-level code:

const updateStream = someOtherStreamLibrary.createStream();
// let's say you need to call updateStream.emit(val) to emit a value.
const update = val => updateStream.emit(val);
// now you can pass update to views as before.

The rest of the code works without any changes. That is the benefit of not tying view code to stream implementation details.

I'm not sure about the term lifting -- I've heard of it, but am not an expert on its meaning -- but nothing special happens when passing update when we pass it to views. It is a function. As I showed above, you could wrap the stream library emit call to be able to pass update such that views can call update(val). If that is called lifting, then yes :) But with flyd it is already a function so it is passed as-is.

Now, about what is passed to update: yes, you can pass a value. But in Meiosis, the value itself is actually a function. update is a stream of functions, as explained in the first bullet in https://github.com/foxdonut/meiosis/wiki/The-Fundamental-Setup#the-meiosis-setup

update is a stream of model updates: that is, a stream of functions that get the current model and return the updated model.

Passing a function that updates the model, instead of passing a value, is very powerful:

Finally, as you said, the anonymous function in the example is mutating the model. Indeed, it is not pure. This is intentional to demonstrate that you can use plain object mutation if you wish. Immutability is not a requirement. But it is certainly possible to use immutable models (Object.assign, Ramda, partial.lenses, Immutable.js, and so on). The choice is yours.

I hope that is helpful!

Thank you for looking at Meiosis! Let me know if I can be of further help.

dmitriz commented 7 years ago

Hi @foxdonut ,

Many thanks for your answers!

I love your work here and it was one of my inspirations for the un project

It seems to have many goals similar to Meiosis. Would be great to know your opinion about it.

Sorry for being short here, I'll be happy to answer in more details once I am sure my answers will reach you :)

foxdonut commented 7 years ago

Thank you @dmitriz for your kind words!

dmitriz commented 7 years ago

Hi @foxdonut

Just wanted to mention, we have some related discussion on some more general composition strategies with models decoupled from the views:

https://github.com/uzujs/uzu/issues/6#issuecomment-304519136

Would be curious to hear your opinion on it.