Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy evaluation #27

Open
dferens opened this issue May 16, 2016 · 8 comments
Open

Lazy evaluation #27

dferens opened this issue May 16, 2016 · 8 comments

Comments

@dferens
Copy link

dferens commented May 16, 2016

Hi,

So basically there is no need to propagate updates to cells which have no listeners.

Imagine you have this cell structure and you are building app which tracks some other cell i. So, when a updates there is no need to compute cells e, f, g and h and we can defer that computation to deref call.
javelin-1

Does it make sense?
Thanks.

@micha
Copy link
Contributor

micha commented May 16, 2016

@dferens Consider this situation (a common pattern in Hoplon applications):

(defc= org ...) ;; some formula cell

(defn login!
  "Maybe does AJAX request, or something..."
  [user pass org]
  ...)

;; The DOM:
(html
  (head)
  (body
    (let [user (cell nil)
          pass (cell nil)]
      (form
        :submit #(login! @user @pass @org)
        (label "User:" (input :keyup #(reset! user @%))) (br)
        (label "Pass:" (input :type "password" :keyup #(reset! pass @%)))))))

Notice how the org formula cell is dereferenced asynchronously, so there are no "listeners" on it. How would something like that work?

@dferens
Copy link
Author

dferens commented May 16, 2016

I'm sorry if I don't understand implementation of javelin properly.
If org cell has no listeners on it, why can't we compute formula once on deref stage instead of computing it multiple times when some source cell updates?

@micha
Copy link
Contributor

micha commented May 16, 2016

We could, but i believe the bookkeeping involved would actually make things slower.

@micha
Copy link
Contributor

micha commented May 16, 2016

Also currently cells can contain state and perform side effects, like this, for instance:

(let [i-saw-a-dog (atom false)]
  (defc= c
    (do (when (= othercell "dog") (reset! i-saw-a-dog true))
        (str "hello, " othercell " i saw a dog is " @i-saw-a-dog))))

Lazy evaluation would make this kind of stateful cell impossible, because you might attach a listener or dereference after othercell has had the value of "dog" once but no longer does at the moment.

@dferens
Copy link
Author

dferens commented May 17, 2016

Can't we introduce another type of formula cell which exposes this lazy behaviour? Like:

(defc~ i (+ a 1))

And it will propagate its value only when there are some other cells watching it, or when some code is deref-ing it.

The goal of this stuff is to increase efficiency . If you have a lot of pure functions over some cell which simply transforms your data, you will compute just what you need.

@fonghou
Copy link

fonghou commented Aug 20, 2016

Just want to share a note that MobX (https://mobxjs.github.io/mobx/refguide/api.html, what a similarity to Javelin!) separates pure computed values (ie. pure formula cell) vs. reactions (ie. side-effecting formula cells). Hence, the former can be evaluated lazily, and the later eagerly.

Here is a blog by MobX's creator that explains the rational and what/how it accomplishes.

https://medium.com/@mweststrate/becoming-fully-reactive-an-in-depth-explanation-of-mobservable-55995262a254#.rprmqffz9

Cheers!

@thedavidmeister
Copy link
Contributor

i think this is relevant #37

rather than introduce a new but still hard-coded propagation mechanism, i'd personally like to see custom/extensible propagations implemented first, then a discussion of what specific propagation strategies should ship with core.

@thedavidmeister
Copy link
Contributor

also this is spot on in the linked article above:

Any imperative action that an application takes in response to a state change usually creates or updates some values. In other words, most actions manage a local cache. Triggering the user interface to update? Updating aggregated values? Notifying the back-end? These can all be thought of as cache invalidations in disguise. To ensure these caches will stay in sync, you need to subscribe to future state changes that will enable your actions to be triggered again.

we can go back and forward on implementation details but everything ultimately boils down to cache management, which AFAIK has no truly general "one size fits all" solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants