
I wonder if this breaks referential transparency. Say, you define a signal s and use s twice in some expression. s may be evaluated once and it maybe evaluated twice. Does this make a difference? I'm not sure about the answer at the moment, but I believe we'd simply get two identically behaving signals in most cases, assuming they are evaluated in the same superstep. The automatic insertion of delays into cycles might cause problems here, as it depends on the current topology of the dataflow network what exactly happens. However, if all the cycles are broken with explicit delays, this is not an issue any more. It would break everything if IO-fed signals were created multiple times, but that can't happen, since their construction is explicitly marked by IO.
Is it possible that thereby the second “instance” has different values than the first one? I can't exclude the possibility, unfortunately, especially with the delay magic. But this is a question that needs further investigation.
When are your signals started? At evaluation time? Yes. Evaluation returns an IORef holding the initial state of the signal, wrapped in some structure. The top-level network is traversed at the beginning, so its nodes are all evaluated just once.
If yes, doesn’t this mean that the meaning of a signal depends on evaluation time so that evaluating the same signal expression twice actually results in two different signals? That's exactly the case. The system heavily relies on sharing in order to work properly. In fact, the ability to keep track of sharing under a pure-looking interface was the main motivation to choose this particular implementation. As far as I can tell, it should be fine as long as no Elerea primitive (signal constructors defined in Internal) is inlined. But I don't like this either, and I'd love to hear how the same effect can be achieved without resorting to such fragile constructs. Unfortunately, all my previous attempts failed at one point or another...
So Elerea seems to have to take special care to not break referential transparency. No matter how I look at it, that would make it a completely different library. One that's very close to Reactive, in fact.
Elerea’s evaluation seems to be driven by hand-coded IO actions so that use of such compiler optimizations is certainly not possible. This is quite true. On the other hand, most calculation is happening in pure functions anyway, and reactive book-keeping probably constitutes only a small fragment of the runtime in a typical application, so this is less of a worry for me. In the end, I see FRP as an alternative way to manage entities, and entities surely can't be optimised away. I use applicative laws to unite pure parts as much as possible, although I don't expect the resulting functions to be as efficient as if they had been assembled at compile time. A JIT compiler surely wouldn't hurt. :)
Grapefruit does this using era type parameters. Elerea doesn’t seem to do anything about this at the moment. Apart from counting on conditions mentioned above, no. The intended meaning is that new signals are created whenever a latcher asks for one, but there's no way to enforce that for the time being.
Patai, could you please correct me where I’m wrong and clarify the points which are still unclear to me? It seems I can hardly say anything new, because you see all the issues perfectly. (By the way, Patai is my family name. Different endianness over here. ;)
What do you think, Grapefruit is lacking, compared to Reactive? Oh, it's just my subjective opinion that I find the interface of Reactive very easy to use and flexible. That's primarily what I wanted to replicate in Elerea, I just ended up getting rid of events altogether, as I didn't need them, and that gave me a simpler system to play with. Grapefruit looks like something that feels more natural when describing the system at a higher level. But I really need to play with it more to have a well-founded opinion.
Gergely -- http://www.fastmail.fm - mmm... Fastmail...