kefirjs / kefir

A Reactive Programming library for JavaScript
https://kefirjs.github.io/kefir/
MIT License
1.87k stars 97 forks source link

Work off of immutable.js? #14

Closed jasonkuhrt closed 9 years ago

jasonkuhrt commented 9 years ago

Just curious if there is any use in building off the foundation of the excellent immutable.js? Would require an internal re-write to do this, but maybe there's a big benefit to leveraging the immutable and equality implications provided by immutable.js.

Thoughts?

rpominov commented 9 years ago

I am not very familiar with immutable.js, so most honest answer would be "I don't know", but I guessing that it'll reduce performance and increase memory usage, and most likely won't bring much simplification to the code.

jasonkuhrt commented 9 years ago

As I dig into both projects more I may have new thoughts but was hoping someone else would have already considered this.

I am not very familiar with immutable.js, so most honest answer would be "I don't know", but I guessing that it'll reduce performance and increase memory usage, and most likely won't bring much simplification to the code.

@leebyron thoughts?

leebyron commented 9 years ago

I'm not very familiar with Kefir, so I may not be able to give great direction here, but I can share some of the strengths and realities of Immutable.js

On performance and memory overhead: yep, there is some overhead in comparison to using raw Array and Object. But persistent immutable datastructures have different complexities for different operations. For example "get" is O(1) for array but O(log32 N) for Immutable List. For moderate sized lists this is effectively O(1). The win comes from copy. Copy an array is O(N) where copy an Immutable list is O(1). Copy with mutation (conj/set) is O(log N) in Immutable's collections where it's O(n) for native collections.

Another important benefit is identity equality, any copy of an immutable collection has identity equality. Any mutation made that results in a no op has identity equality. Very obvious fact: when two copies have identity equality, they always have value equality. This is incredibly powerful.

a = {foo:"bar"} b = a doWork(a); a === b // are they value equal?

When you consider this example complected with time, you see how even though we take identity equality meaning value equality for granted with raw objects, subtle bugs can appear if doWork() mutates a and our identity check was hoping to learn if any changes had been made. Instead we need to do:

b = copyObj(a) doWork(a) isDeepEqual(a, b)

We've made a defensive copy so we can expensively compare it later. The former example would have Just Worked with Immutable datastructures. If a was a very large data structure, this could be a huge burden lifted.

That describes the primary benefit of these data structures. If you find code making defensive copies because a called function may mutate data, or making a copy of a data structure because you need to make some changes to it before passing it elsewhere and you don't want to change the original, or you want to make a very cheap comparison to know for sure that data hasn't changed (super useful in concert with React's shouldComponentUpdate)

This comes down to making sure these tools are a good fit for your problem. Immutable.js is not a panacea. But it is a power tool to solve these specific kinds of problems and often the wins can dramatically outweigh the overhead.

leebyron commented 9 years ago

TL;DR: Immutable data decouples value from time, and this is what allows code to become easier to understand and why functional environments (haskell, Scala, Clojure) prefer them as the native data structures.

jasonkuhrt commented 9 years ago

TL;DR: Immutable data decouples value from time, and this is what allows code to become easier to understand and why functional environments (haskell, Scala, Clojure) prefer them as the native data structures.

Yep!

rpominov commented 9 years ago

Very interesting stuff! I will keep it in mind, and will look where immutable data structures might be helpful in Kefir. For now, as far as I remember, operations where Immutable.js wins over native data structures (copy, comparison) are not used frequently in Kefir. And I don't sure it is a good idea to add a dependency to another library if it will be used only in couple cases.

Also performance is one of my primary concern. I am ok with sacrificing code simplicity in the library to give users ability to think less about performance in their application code. So using Immutable.js to make code easier to understand also not what I would do if it brings any noticeable performance overhead.

leebyron commented 9 years ago

I support that. Only use it if it's the right tool for you and helps you solve a real (performance) problem.

jasonkuhrt commented 9 years ago

Thanks for your thoughts guys!