@dustingetz I think the idea of continuous bind makes sense. I've opened an issue https://github.com/leonoel/missionary/issues/26
What is the expected state of discrete bind in missionary today, is that something that is working or close to working do you think?
the 3 discrete bind operators are useful and they're working well
Does it recompute the heights of the nodes when the DAG is adjusted inside a bind-like operation
recompute the heights means what to you ?
C-f for "height"
its the topo ordering mechanic
I'm not sure the ordering strategy is the same
The key sentence is > Bind nodes introduce special height constraints, so that stabilization is guaranteed to recompute the left-hand side of a bind before recomputing any node created by the right-hand side [f]. This avoids recomputing nodes created on the right-hand side that would then become unnecessary when the left-hand side changes. More precisely, in [t >>= f], any node created by [f] is made to have a height larger than [t].
I think I have a failing test case caused by this
reactor
has bugs so it's totally possible, I'd love to see your test case though
(tests
"discrete bind: harder version"
(def !p (atom 1))
(def !q (atom 2))
(def !control (atom :p))
(def >p (m/watch !p))
(def >q (m/watch !q))
(def >control (m/watch !control))
(def >cross (bind >control (fn [c]
(case c :p >p :q >q))))
(def >z (m/zip vector >p >q >cross))
(def !z (>z #(println :ready) #(println :done)))
@!z => [1 2 1]
(reset! !control :q)
; never ready, so below this point is undefined
; expected:
@!z => [1 2 2] ; fail
(reset! !control :p)
@!z => [1 2 1] ; fail
)
This is the same as the above test case except
(def >z >cross #_(m/zip vector >p >q >cross))
this line(defn bind [>a f]
(ap (?! (f (?! >a)))))
zip
with continous flows is weird
what is it supposed to model ?
i don't think zip makes sense in continuous signals
I think about this differently than you, I think in terms of the typeclasses - fmap (variable arity), and bind. I think that's all i need
so just m/latest and bind
I think you can emulate continuous bind with (defn bind [>a f] (m/relieve {} (m/ap (m/?! (f (m/?! >a))))))
latest
for fmap
is fine
Im realizing my test is malformed, i'm mixing continous and discrete primitives again
omg it passed
lol
(tests
"discrete bind: harder version"
!! (defn bind [>a f] (ap (?! (f (?! >a)))))
!! (def !p (atom 1))
!! (def !q (atom 2))
!! (def !control (atom :p))
!! (def >p (m/watch !p))
!! (def >q (m/watch !q))
!! (def >control (m/watch !control))
!! (def >cross (bind >control (fn [c]
(case c :p >p :q >q))))
!! (def >z (m/latest vector >p >q >cross))
!! (def !z (>z #(println :ready) #(println :done)))
@!z => [1 2 1]
!! (reset! !control :q)
@!z => [1 2 2]
!! (reset! !control :p)
@!z => [1 2 1]
)
switched zip to latest and it passesalso your example is a tree, not a dag so I'm not sure how it relates to the height recompute stuff
it's meant to join all signals back together at >z
Using (defn bind' [>a f] (m/relieve {} (ap (?! (f (?! >a)))))) also passed
the height test i think is that the bind-choice (>p or >q) must have a height greater than >control > More precisely, in [t >>= f], any node created by [f] is made to have a height larger than [t].
As I understand it, what matters is that if both t
and latest result of f
have changed when the value is sampled, t
takes priority and f
must be recomputed to get a fresh node.
Node height is an implementation detail of incremental
related to dag propagation order, it's not relevant in missionary
because the dag part (`reactor`) is decoupled from flow composition.