Giter VIP home page Giter VIP logo

Comments (12)

vlaaad avatar vlaaad commented on May 25, 2024

Hmm, you could compose key subscriptions with function subscriptions, like in this example:

(defn user-sub [ctx user-id]
  (get (fx/sub ctx :users) user-id)

(defn user-name-sub [ctx user-id]
  (:name (fx/sub ctx user-sub user-id)))

from cljfx.

vlaaad avatar vlaaad commented on May 25, 2024

So the idea is to use more granular subscription functions to build a subscription dependency graph from the whole app state map. Don't forget to use core.cache, or context cache will eat all your memory!

from cljfx.

kxygk avatar kxygk commented on May 25, 2024

Yeah, so this did occur to me... but does this have the intended effect ? Wouldn't any change in any of the :users trigger an update on everywhere you are subscribed to a :name?

Or is the idea that it would trigger an update, but once you hit

(get (fx/sub ctx :users) user-id)

What you got back would be identical and the update would terminate prematurely and return a cached value ?

from cljfx.

vlaaad avatar vlaaad commented on May 25, 2024

Exactly!
Going from {:users {0 {:name "geokon"}}} to {:users {0 {:name "geokon"} 1 {:name "vlaaad"}}} will require recomputation of (fx/sub ctx user-sub 0) subscriptions, but since that will return the same value as before, it won't require recomputation of (fx/sub ctx user-name-sub 0). It is described in Subscriptions and Contexts readme section :)

from cljfx.

kxygk avatar kxygk commented on May 25, 2024

Alright! I was just worried I misunderstood. I guess the term "subscription" is a bit of a misnomer b/c an update of the state/context is not triggering the subscribers in any sense - and if I understand correctly, all the subscriptions are rechecked on each GUI tree regeneration.

In my mental model I thought it was an actual subscription in that each node's subscriptions are recursively traversed to get a dependency list and only affected branches of the trees are regenerated when values are updated (that would be very tricky to implement haha)

from cljfx.

vlaaad avatar vlaaad commented on May 25, 2024

In my mental model I thought it was an actual subscription in that each node's subscriptions are recursively traversed to get a dependency list and only affected branches of the trees are regenerated when values are updated (that would be very tricky to implement haha)

It is like that! For every subscription function, the context tracks its dependencies and then uses these dependencies to determine if it should call subscription function again or use cached result if results for dependencies didn't change. It was super tricky to implement!

from cljfx.

kxygk avatar kxygk commented on May 25, 2024

haha, yes it does sounds really tricky, but it's elegant the way it works! :)

The "issue" is that at the end of the day every subscription needs to be get checked to get that cache hit (like in the example you gave - you get the user, see it's the same user - return the cached name) so that you can redraw the whole GUI tree

What I had misunderstood was a bit different haha. Imagining you only are subscribing to keys (subscription functions would be and added complication on top), you could have every key in the context/state has an associate list of subscribed nodes. When the key is modified then the associated nodes are regenerated. The rest of the tree would just be left as-is and wouldn't need to be checked

But in way I actually like your solution more. The subscriptions and context are decoupled from the GUI tree. There are no assumptions about the data structure of the state. You can test it directly and it's much easier to reason about. The overhead is negligible. It's just my C++ brain that inverted the direction of control here :)

Thanks for walking me through it. I now get how to get this all plugged in (famous last words :)) . As always I really appreciate your help here. This is going to greatly clean up my code

from cljfx.

vlaaad avatar vlaaad commented on May 25, 2024

You are welcome :)

You got me thinking about contexts, so I released a small update: 1.6.9 :D

One thing I changed is I fixed invalidation mechanism for contexts so it does not require iterating over all existing cache entries when new context is created from the old one. That's a minor performance improvement that bothered me with contexts for a long time.

Another, more noticable change, is that you can now subscribe to just a context itself with (fx/sub ctx), so you can have full access to the context if you need it.

from cljfx.

kxygk avatar kxygk commented on May 25, 2024

Oops. Looks like force-pushes don't kill commits permanently.. Typoes are immortalized in Github :)

Thank you some much again for the help. It took me quite a while to refactor everything with contexts/subscriptions - but the end result is significantly better and very decoupled. I'm extremely pleased with the result!

Once I deal with packaging (I get weird issues with jdeps/jlink - have you had any luck minimizing the bundled run time?) I'll need to work on the meta data for the project now (README/wiki/page etc.) and I'll make sure to mention you. This project deserves a lot more traction

from cljfx.

vlaaad avatar vlaaad commented on May 25, 2024

You are welcome! :)

I didn't look into minimizing bundle size, I would start with looking at created uberjars to determine what takes so much space. One thing that comes to mind is org.openjfx/javafx-web that cljfx depends on by default — it has a packaged browser engine (webkit) that you can exclude if you aren't using web views in your cljfx app.

from cljfx.

kxygk avatar kxygk commented on May 25, 2024

Yeah, this I've already done. It's a bit messy, you run clj -Stree and then sorta guess which parts you probably aren't touching. Then you can add exclusions in your deps.edn.

So I end up with:

{:deps
 {org.clojure/clojure {:mvn/version "1.10.0"}
  org.clojure/core.cache {:mvn/version "1.0.207"}
  org.boofcv/boofcv-core {:mvn/version "0.35"
                          :exclusions [org.boofcv/boofcv-recognition]}
  cljfx {:mvn/version "1.6.9"
         :exclusions [org.openjfx/javafx-web
                      org.openjfx/javafx-media]}
  org.clojure/data.csv {:mvn/version "0.1.4"} ;; data injestion
  thi.ng/geom {:mvn/version "1.0.0-RC4"
               :exclusions [org.clojure/clojurescript
                            org.jogamp.jogl/jogl-all]}
  guru.nidi.com.kitfox/svgSalamander {:mvn/version "1.1.3"}
  }

I'm sure you can get a higher graularity.. but past a certain point you stop seeing large dividends in terms of file size.

I tried to minimize the uberjar with Proguard but I think b/c Clojure's late binding it just can't figure out what's removable.

The issue is that theoretically jlink is supposed to just give you the parts of the runtime you need/use. But with all this zillions of unused vestigial files left around (that you didn't catch in the exlusion) jlink ends up sort of including the while runtime - ie all the modules. And running jdeps produces a crash related to cljfx/javafx - so I'm not entirely sure how to inspect things further. I think I'm going to have to just accept that the application will never get to an "emailable" size and it will have to be hundreds of megabytes and hosted on github :) But if you do ever look into this further please do let me know!

from cljfx.

kxygk avatar kxygk commented on May 25, 2024

For reference the starting uberjar is down to about 20MB

And well I just tried to create an AppImage and it turned out to be 1.1GB with ~44,000 files inside haha. It does launch very quickly though ;)

Making an AppImage with the AOT version is ~200MB with ~4,000 files

from cljfx.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.