`Behavior a -> Event b -> Dynamic a` please?
Really i was looking for (a -> b -> c) -> Behavior a -> Dynamic b -> Dynamic c. Both are expressible via some combination of updated, attachWith, current, sample and buildDynamic, but i'd prefer something a bit higher-level.
(i base this mostly on the quickref, so perhaps i am missing something that isn't there.)
I've wanted this before as well. But I'm not sure there is a "safe" way of doing it (that doesn't suffer from high likelihood of cycles or being wrong).
@lspitzner What are the semantics you're looking for? E.g.: if we produce out using this, when should updated out fire, and what should be the value of current out at all times?
I would imagine
mkDynamic (current x) (updated x) == x.
I thought about these some more, and I think these functions can't be written.
In the case of f :: (a -> b -> c) -> Behavior a -> Dynamic b -> Dynamic c, suppose we do: dab = f (,) ba (pure b). The only Event in sight is updated (pure b), which is equivalent to never. Therefore, the updated dab cannot fire, since we can't spontaneously generate Event occurrences in pure code. The "Dynamic law" is that forall d, current d changes when and only when updated d fires, so current dab cannot ever change. This means that dab = pure ab for some ab = (a, b) :: (a, b). However, the only way we can get an a is from ba, and this is a pure function, which means it must operate identically regardless of when it is evaluated. As a result, it cannot choose any particular time at which to sample ba. Since ba may not be constant, a is ill-defined.
So, I believe that the only acceptable implementation for this function involves supplying undefined for a at least some (I would guess all, actually) of the time.
Note that this argument makes critical use of the Dynamic law, so, as you might expect, unsafeBuildDynamic allows you to get around this. This will only work properly if there is some special relationship between ba and the Dynamic b that ensures that dab will be lawful - and these things get really subtle.
For mkDynamic, the proof is simpler: in mkDynamic (current x) (updated x) == x', the only a we have is from current x :: Behavior a, since the second argument has type Event b (and a is not generally the same type as b). Since Behaviors are never prompt, we cannot produce updated x' == updated x.
What if mkDynamic :: Behavior a -> Event a -> Dynamic a? This is actually the function I've wanted. I've wanted something almost like holdDyn but that takes a Behavior a instead of a as its initial value. That's what this would be, effectively.
The function with that exact type signature is unsafeDynamic. It's unsafe because it has no way to know whether your Event and Behavior obey the Dynamic law.
buildDynamic, on the other hand, is monadic, and I think it's got almost exactly the semantics you're describing. If you write d <- buildDynamic (sample b) e, you get a Dynamic whose value is the same as b when it's created, and changes when (and only when) e fires. It's the same semantics as v0 <- sample b ; holdDyn v0 e, except that it's lazier (with buildDynamic, the sample b is forced either when current d is forced or before the next frame, whichever comes first - whereas with the other approach, it's forced immediately).
Aha! Thank you. I did notice that unsafeDynamic fit the bill but I didn't want to use it for the obvious reason. But using buildDynamic seems like a great way as well!
FWIW, I've found that unsafeDynamic is surprisingly difficult to use properly. I wouldn't recommend it if you have any other options.
It's aptly named. I don't think I'd ever dare to use it.
@ryantrinkle Right.
What about the versions that return monadic values? Are the following bad idea? (The names may be chosen badly, and I dislike the RankNTypes, but otherwise?)
dynamicPushAlways1
:: (R.Reflex t, R.MonadHold t m)
=> R.Dynamic t a
-> (forall m' . R.MonadSample t m' => a -> m' b)
-> m (R.Dynamic t b)
dynamicPushAlways1 ad f =
R.buildDynamic (f =<< R.sample (R.current ad)) (R.pushAlways f (R.updated ad))
dynamicPushAlways2
:: (R.Reflex t, R.MonadHold t m, MonadFix m)
=> R.Dynamic t a
-> (forall m' . (R.MonadHold t m', MonadFix m') => a -> m' b)
-> m (R.Dynamic t b)
dynamicPushAlways2 ad f = do
b0 <- f =<< R.sample (R.current ad)
R.foldDynM (\a _ -> f a) b0 (R.updated ad)
dynamicAttachWith
:: (R.Reflex t, R.MonadHold t m)
=> (a -> b -> c)
-> R.Behavior t a
-> R.Dynamic t b
-> m (R.Dynamic t c)
dynamicAttachWith f ab bd = do
a0 <- R.sample ab
b0 <- R.sample (R.current bd)
R.holdDyn (f a0 b0)
(R.pushAlways (\b -> R.sample ab <&> \a -> f a b) (R.updated bd))
dynamicTag
:: (R.Reflex t, R.MonadHold t m)
=> R.Behavior t a
-> R.Event t b
-> m (R.Dynamic t a)
dynamicTag ab be = do
a0 <- R.sample ab
R.holdDyn a0 (R.pushAlways (\_ -> R.sample ab) be)
(edit 1: removed some unnecessary constraints, make dynamicTag use Event in input)
(edit 2: Fix constraints in dynamicPushAlways1/2; they are different after all (!))
@lspitzner Can you provide some examples of the situations where these would be used?
No, not really :/ Most of the time one can invent some initial values so creating a Dynamic from some pushAlways'd Event is trivial. I'll have to look out for some good examples.
My general sense is that, if I found myself needing dynamicPushAlways and dynamicAttachWith, I'd wonder whether I had made a mistake in some of the surrounding code. That's not to say that they're wrong, bad, or anything like that, just that it's tough to imagine when I'd need them.
dynamicTag is a bit more obvious, in that it seems to just sample the given behavior at creation and then whenever the given event fires. I can't think of a use case off the top of my head, but I suspect there are some. In this case, the question is just: can we think of a name that is shorter and more understandable than the implementation itself, and document it well? Having concrete use case examples would help with that, I think. Here's a better (lazier) implementation, by the way:
dynamicTag b e = buildDynamic (sample b) $ tag b e