-
-
Notifications
You must be signed in to change notification settings - Fork 2
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
Flows #25
Conversation
src/hti/re_dash.cljd
Outdated
(reduce (fn [new-db {:keys [inputs output path]}] | ||
(let [new-db-resolved-inputs (resolve-inputs new-db inputs) | ||
old-db-resolved-inputs (resolve-inputs @app-db inputs)] | ||
(if (not= new-db-resolved-inputs old-db-resolved-inputs) | ||
(assoc-in new-db path (output new-db-resolved-inputs)) | ||
new-db))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looks good to me!
liveness check would run here? (cleanups...?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so yes. Still need to consider all the supporting functionality around the Flow lifecycle, while keeping in mind things are still settling in re-frame land around this feature.
src/hti/re_dash.cljd
Outdated
([flow-id {:keys [inputs output path] :as flow}] | ||
(when-not flow-id (throw (ex-info "flow-id is mandatory" flow))) | ||
|
||
(let [flow' (assoc flow :id flow-id)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking at this it occurred to me if it might be worth using records for these object shapes?
(defrecord Flow [id inputs output path])
(and possibly similar with interceptors etc)
In JVM Clojure field access on records is much faster than map lookups, though I don't know if that applies in cljd
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be interesting to do some performance bench-marking at some point, and compare between using maps vs records.
My view is that it would need to be a significant, measurable, performance gain before we swap the simplicity of maps for records
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel records would make anything that much more complicated though.
In Clojure they're just glorified maps anyway
src/hti/re_dash.cljd
Outdated
(or (flow-id definitions) | ||
(throw (Exception. (str "Flow not found: " flow-id))))) | ||
|
||
(defn- derived-input-path |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe worth splitting namespaces at this point? it't getting pretty crowded
src/hti/re_dash.cljd
Outdated
{::flow<- flow-id}) | ||
|
||
(defn- sort-flow-ids | ||
"Sort all Flow definition dependency graphs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all? do we have to worry about cyclic dependency between flows that aren't live?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I think we do, as flows may come alive at any point during flow processing given their live?
test passes.
I've started with the flow life-cycle hooks so it will get clearer as we progress.
src/hti/re_dash.cljd
Outdated
(throw (Exception. (str "Subscription not found: " subscription-id)))))) | ||
(if (flow? query-vec) | ||
|
||
(flows/subscribe query-vec) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does it mean to subscribe to a flow?
My understanding was that flows are app-db -> app-db processes. They respond to changes in their inputs and generate some output into the app-db in turn. The user would subscribe to the output field in the app-db (or whatever else they use instead of the app-db)
Especially if a flow can have multiple outputs, how would that work with direct subscribe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the docs, a Flow can only have one output path.
I suspect the intention is merely to provide a more declarative way to subscribe to the Flow's output path by referencing it by name (:id)
It's also kind of nice that the Flow's logic can therefore be updated later without affecting the UI that subscribes to it necessarily.
src/hti/flows.cljd
Outdated
(defn subscribe | ||
[query-vec] | ||
(let [path (path query-vec)] | ||
(f/$ (get-in (f/<! app-db/app-db) path)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think it's a good idea to hard-code the app-db here.
Subscriptions can use anything subscribable if you give them an according input-signal when building them with reg-sub. I think a similar pattern would be desirable here, so that it would be possible to use alternative backends for the app-db (or multiple app-dbs altogether).
Imagine something like this: using one (atom {})
(let's call it data-db) to store user data and using the app-db to store UI state in parallel. This is already possible with subscription input signals. With flows it would be possible to automatically update the app-db based on changes to the data-db (from the result of an API call or something). That'd be awesome.
On the other hand, using input functions instead of "just paths" would make the current dependency calculation pretty much impossible... Maybe if there was a protocol for app-db-like things?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The philosophy of re-dash is to take lead from re-frame as far as reasonably possible in my opinion.
Anything outside of that, I'd argue should be plugable at some extension point, like using interceptors for example.
These could maybe be published as supplementary libraries like re-dash-my-custom-db
or something. Just thinking out loud.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes that's exactly what I meant.
By hardcoding app-db here subscribing to Flows becomes wired to the app-db.
It'd be nice if (while the app-db was the obvious and simple default) it'd be possible to provide a different input-signal (in reg-sub terms) at reg-flow time
src/hti/re_dash.cljd
Outdated
:else (f/<! signals))) | ||
query-vec)) | ||
(throw (Exception. (str "Subscription not found: " subscription-id)))))) | ||
(if (flow? query-vec) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If subscriptions and flows were records subscribe could be turned into a protocol on top of that (which could also protect against the in my experience all-too-common silly mistake of (subscribe ::id)
)
I'd be happy to help with implementing that if you want a hand
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're welcome to put a proof of concept together, thank you for offering.
As I'm still struggling a bit to see the benefit records will bring, my two biggest concerns with whichever end result we end up with, would be:
- that it significantly, and measurably improve event/subscription performance over the current implementation
- that it does not add complexity the code
Also just be aware that things are still changing as I'm iterating through the Flow implementation
samples/flow/src/acme/model.cljd
Outdated
{:clear-flow (:id area-flow)})) | ||
|
||
;; Initialize db | ||
(rd/reg-event-db ::init (fn [db _] (-> db |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the indentation broken here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thx, fixed
;; the terms of this license. | ||
;; You must not remove this notice, or any other, from this software. | ||
|
||
(ns alandipert.kahn |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a link to the original gist? Not sure
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add the link to the gist anyway
@@ -0,0 +1,41 @@ | |||
# Flows | |||
|
|||
Status: (alpha) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does that mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
re-frame released Flows under alpha, likely for reserving the right to either change the API, or how it works, or bin the whole idea if it doesn't work out.
re-dash would track this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK so this means "API might change, there be dragons"
Some places call "untested experimental code" alpha, that's why I asked
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this doesn't have to move.
It's a huge breaking change (literally obliterates the whole existing public API) for the sake of folder structure...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was not to break but to deprecate.
But I see your point, it is rather harsh for the sake of folder structure. I'll revert this namespace
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imo, either keep it at hti.re-dash, or move it to hti.re-dash.core and add a "new" hti.re-dash that re-exports the (at least current) public API from the new hti.re-dash.core
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's precisely what the hti.re-dash
change in my commit proposed - it exports everything from core but with a deprecation notice on the namespace.
Would removing the deprecation notice then be a more acceptable middle ground?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Am I missing something? I can't seem to find the diff you're talking about. The hti.re-dash namespace is just gone
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OH never mind. It was just Github being stupid (it showed hti.re-dash as all deleted and hti.re-dash.core as all created)
{:deprecated "0.9.0" | ||
:superseded-by "hti.re-dash.core"} | ||
(:require [hti.re-dash.core :as core])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me
Closes #23
Adding support for Flows
New sample app
samples/flow