4 ms·
The "destiny" operator is just function definition. It might seem different if you're only used to languages where function calls and variable accesses are synt
by ef4 12y ago
The "destiny" operator is just function definition. It might seem different if you're only used to languages where function calls and variable accesses are syntactically distinct.
- throwaway283719 12y agoNot true. Consider the following snippet - a = 10 b = function() { return expensive_computation(a) } Now, even if you can call b without parens, it still has to redo the expensive calculation every time you access it (because a might have changed... even if it hasn't). The "destiny" operator is more like a = 10 b = function() { persistent cached_value = null if is_null(cached_value) or has_changed(a) { cached_value = expensive_computation(a) } return cached_value } but instead of that, you get to write (in this totally fictional programming language) a = 10 b <- expensive_computation(a) and have the compiler take care of the caching and updating for you.
- Sharlin 12y agoPlus the real power of reactivity is that it is push, not pull. The changes are propagated without ever needing to poll for the new value manually - for instance, you could display the value of b in a UI and have it change whenever the value of a changes.
- restalis 12y agoIsn't the push strategy an expensive one? Maybe not all of the dependencies are needed right away, why bother with dispatching a new value though all of them? ...and more, considering that distinct dependency updates may require different (and admittedly expensive) computations, won't the push way be an overkill?
- taeric 12y agoI'm not sure pull is any better in this regard. Simply stated, naive push and pull are both bad. You wind up putting a fair bit of heuristics on top of either to make them good. Pull has the major downside that you never know when you need to pull a new value. Which is a pretty major downside. And really, this does just feel like we baked some new semantics onto the observer pattern.
- Sharlin 12y agoIt depends on your point of view. From one perspective, the observer pattern is simply a way to implement reactive values in the OO paradigm - indeed, a somewhat arrogant FP aficionado would describe many of the GoF design patterns as clumsy OO emulations of things a functional language would be able to express much more elegantly. In the Rx-style reactive model the big thing is making the event streams, in addition to the events, first-class objects, so they can be composed and manipulated with the usual FP subjects like map/filter/reduce. This makes, for instance, throttling to optimize the propagation of changes almost trivial compared to the situation where you only have individual events without context.
- taeric 12y agoYeah, I've read some of the papers. I was even heavily swayed for a time. However, I'm not entirely sure that Streams are an automatic win in abstraction. Often combining streams is just a very clumsy way of saying "add this and that." More specifically, I think things are a lot easier when you can work way above those abstractions. As soon as you get into the weeds, all of the abstractions suck. I also think this is specifically why people like angular so much. For many use cases, you are just setting values and having the UI present said values. Note, setting values, not appending to streams. Now, do I expect that angular's internals would benefit from the streams metaphor? I certainly suspect so. Don't know, though.
- tel 12y agoThere are also push-pull semantics which try to the the best of both worlds.
- ef4 12y agoNothing I said implies either push or pull. Sufficiently smart runtimes can do both based directly on function definitions. Even Excel knows how to bind a displayed value directly to functional dependencies and only recalculate when necessary. I'm not denying these techniques are useful, I'm just pointing out that a lot of what seems nice about them is actually attempts to reimplement functional programming inside languages that don't do it very well natively.
- tinco 12y agoA proper function will always return the same value if you give it the same input, so there's no need for the computer to always re-run the expensive computation.
- seanmcdirmid 12y agoRight. Without state using only pure functions, you get to ignore change and time, therefore even the notion of "reactive programming" is not very meaningful.
- tinco 12y agoThat functions are pure means that their inputs and outputs are well defined, not that they ignore or don't work with things like state or time. Before monads were 'invented' for functional programming, reactive programming (or stream based I/O) was the only way of handling state and side effects in Haskell.
- dragonwriter 12y ago> Now, even if you can call b without parens, it still has to redo the expensive calculation every time you access it (because a might have changed... even if it hasn't). That's true in most programming languages, but not in languages that distinguish side-effect free code. (Such a language implementation might still rerun a pure function because it doesn't include a way of "remembering" that a function has been called with particular arguments before, but that's an implementation detail.)
- zak_mc_kracken 12y agoThat's called memoization and you usually want to leave this decision up to the caller and not the callee since only the caller knows if they are fine with receiving the same value as calculated previously or whether they want a new one each time.
- louthy 12y agoOnly relevant if the function isn't pure.
- ef4 12y agoYou're reinforcing my point. You're assuming imperative semantics. Wherever "destiny" would be appropriate, you necessarily have a referentially transparent function. Wherever you have a referentially transparent function, a smart compiler or runtime can avoid recalculating anyway, without bothering the programmer with the distinction between "function" and "destiny".