<div dir="ltr">I'm getting a bit confused with too many low-level details. Some of my responses are educated guesses, so maybe there's implementations that manage to circumvent what I say. Please, correct me where I'm wrong.<br><br><div><div class="gmail_extra"><div class="gmail_quote">On 19 February 2018 at 03:31, Ertugrul Söylemez <span dir="ltr"><<a href="mailto:esz@posteo.de" target="_blank">esz@posteo.de</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">> I suspect I may not be understanding precisely what you mean. Perhaps<br>
> you can describe this in more detail or with an example?<br>
<br>
</span>The easiest way to see the difference is by looking at some of the<br>
combinators. Notice that things like 'hold', 'scan'/'accum', and 'tag'<br>
are real functions. In a first-class FRP system these would have types<br>
like the following:<br>
<br>
hold :: a -> Event a -> Moment (Behaviour a)<br>
scan :: a -> Event (a -> a) -> Moment (Event a)<br>
tag :: Behaviour (a -> b) -> Event a -> Event b<br>
<br>
The Moment monad is not inherent to the way the underlying state machine<br>
is constructed, but acts merely as a provider for the notion of "now".<br>
Since 'tag' doesn't need that notion, it's a completely pure function.<br></blockquote><div><br></div><div>Well, in a way. Yes, it can be a pure function, and an event can somehow be a delayed computation of how/when it is actually produced, computed/consumed the moment you want to actually evaluate the network.<br><br>Saying that they are pure would be just fine if Behaviours did not depend on the outside world (that is, if they were "calculated" from pure haskell functions). But I don't think they are. Not always. Not if you want to depend on any external user input.<br><br></div><div>In Reflex (and I'm not trying to discuss the particularities of this implementation), yes, Behaviour and Event are types in a family, but the actual definitions in Spider I can seee are records of IORefs with bangs. Far from pure.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
You can have that function in AFRP as well:<br>
<br>
fmap :: (a -> b) -> Event a -> Event b<br>
<br>
However, unlike 'fmap', 'tag' makes sense in a pure context. You can<br>
pass an Event and a Behaviour to a different thread via an MVar, combine<br>
them there, then send the result back, and it will still work in the<br>
context of the greater application (no isolated state machines).</blockquote><div> </div><div>I don't see how you cannot do that with wires. For instance, you can send a Wire m () (Event b), and a Wire m () (a -> b), and compose them in a pure context. Then you can bring that back and use it.<br></div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"> You<br>
can hold an event in any concurrent thread, etc.<br></blockquote><div><br></div><div>Can you use it without doing IO and executing the computation associated to calculating/polling the behaviour? If so, it must be because the FRP evaluation method has some inherent thread-safety (I you need IO + more for that). Wouldn't you be able to put that thread safety in your monad, and then use it with MSFs/Wires?<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
Another example is that if the underlying monad is nontrivial (say IO)<br>
you can't easily split behaviours in a pure context in AFRP.</blockquote><div><br></div><div>You can, but you need a monad such that: (,) <$> ma <*> ma == (\x -> (x,x)) <$> ma.<br><br>Is this called idempotent?<br></div><div><br></div><div>But to implement any form of Classic FRP or Reactive Programming on top of MSFs, you want that kind of monad.<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"> This<br>
restriction does not exist in first-class FRP:<br></blockquote><div><br></div><div>Well, it is not exposed to the user, but someone must have thought about it and solved it. Duplication of effects is inherent to having monadic computations associated to obtaining the values of behaviours. If you don't cache for a given timestamp, you duplicate effects.<br><br>The same mechanism they used could be applicable to your monadic AFRP variant.<br><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
unzipB :: Behavior (a, b) -> (Behavior a, Behavior b)<br>
splitE :: Event (Either a b) -> (Event a, Event b)<br>
<br>
In AFRP you always have to do it in the context of the underlying state<br>
machine, i.e. MSF/SF/Wire, which means that AFRP forces you to manage<br>
all data structures holding reactive values as part of it or, again,<br>
have isolated state machines. With first-class FRP there is nothing<br>
wrong with keeping a data structure of behaviours in an MVar and have<br>
two concurrent threads modify it:<br>
<br>
MVar (Map K (Behaviour String))<br></blockquote><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
AFRP requires the following instead, and unless all changes are planned<br>
within the state machine and communicated via 'A' changes actually build<br>
up in terms of complexity (you can't just keep composing it with more<br>
and more MSF actions for free):<br>
<br>
MVar (MSF IO A (Map K String))<br></blockquote><div><br></div><div>I could be wrong, but I think most of your problems go away with the kind of monad I mentioned.<br></div><div><br></div><div></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
<br>
Let me make clear that you can express all of these things in AFRP. In<br>
fact it's easily more powerful than first-class FRP, because if the<br>
system exposes it, you get full access to the expressivity of what is<br>
basically a generic state machine. Just to provide one example: there<br>
is no first-class counterpart to 'manage' from wires, because that<br>
combinator only really makes sense in the context of the underlying<br>
state machine. But all of this comes at the expense of giving up<br>
first-class behaviours and events in the above sense.<br>
<br>
If this still doesn't convince you, I strongly suggest that you give<br>
reflex a try. It has a very similar controller interface to AFRP<br>
(stepWire/unMSF) in that it gives you control over the main loop, so it<br>
shouldn't feel too alien.<br>
<br>
As a final remark due to all these issues and more with AFRP most of my<br>
research in the past few years went into getting rid of the A while<br>
retaining most of its advantages.</blockquote><div><br></div><div>I cannot say I like arrow notation, or inputs based on tuples. We need more work on this.<br><br></div><div>However, I decided to embrace the A and I am finding a lot of extensions and guarantees that are possible, or easier, thanks to that.<br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"> [...]<br><br></blockquote><div><br></div><div>Ivan <br></div></div><br></div></div></div>