more concerns about timing semantics

72 views
Skip to first unread message

Janis Voigtländer

unread,
Jan 1, 2015, 3:07:09 PM1/1/15
to elm-d...@googlegroups.com
The other thread (https://groups.google.com/d/msg/elm-discuss/AWx51F_WzUE/iKBdMM94JJoJ) made me wonder about other occurrences of timer.now() in the runtime.

In particular, here and here, but also in Native/Touch.js.

Am I right to assume that the expected semantics of

test : Signal (Time,Time)
test = timestamp (every second)

for everyone is that this signal always carries pairs where first and second component are identical?

The current implementation does not guarantee this, I think. (Actually, the two components being different will happen much more seldomly than the differences I provoked in my example in the other thread, but it can happen.)

Laszlo Pandy

unread,
Jan 1, 2015, 4:21:39 PM1/1/15
to elm-d...@googlegroups.com
The thing I always wondered about the time module is what is the expected output of two instances of fps:
main = Signal.map asText (Signal.map2 (==) (Time.fps 30) (Time.fps 30))

I would expect them to give at the same time value every time, but like Janis's example they do not.

However this can't always work if the arguments are not the same:
main = Signal.map asText (Signal.map2 (==) (Time.fps 15) (Time.fps 30))


Should there be a global ticker which samples at 30 fps but only fires every second time for the 15 fps? What if there is no common factor between the two numbers?

--
You received this message because you are subscribed to the Google Groups "Elm Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elm-discuss...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Janis Voigtländer

unread,
Jan 2, 2015, 5:15:03 AM1/2/15
to elm-d...@googlegroups.com
Actually, I don't think there should be any expectations about two different occurrences of Time.fps 30 running in lock-step. Ensuring that would be just too hard, requiring global synchronization. But:

1. Yes, having two signals both defined as Time.fps 30 not produce identical events is a kind of violation of referential transparency. Likewise with calls of Time.every. Probably that should be mentioned in the documentation.

2. Time.fps does have an issue analogous to the one concerning timestamp (every second). Namely, in timestamp (fps 30) the deltas one obtains by comparing subsequent timestamps in the first pair components may contradict (slightly) the deltas one sees in the second pair components.

3. These issues have to do with the timer.now() calls in here and here, and are independent of the issue solved by my PR in the other thread.

Jeff Smits

unread,
Jan 2, 2015, 5:21:26 AM1/2/15
to elm-discuss
fps 30 == fps 30 is a matter of referential transparency and should therefore be guaranteed. Syncing fps 30 and fps 15 events that coincide is not such a strong requirement, but would be nice. Same with timestamp (every second), would be nice if it were a doubled value, but that's not going to harm referential transparency so it's lower priority.

Janis Voigtländer

unread,
Jan 2, 2015, 5:33:44 AM1/2/15
to elm-d...@googlegroups.com
Well, but guaranteeing that fps t1 and fps t2 behave identically when t1==t2 will be mighty hard, given how the argument type to fps is number, so could be floats, which means that in theory you could end up (to make said guarantee)  having to build up a quite large table of occurrences of fps-calls, to make the required synchronization work. (That is - the table building - if the runtime is establishing that guarantee dynamically. I don't see that the compiler could set it up statically: bla bla, Halting problem while computing the arguments to all fps-calls occurring in the program text, etc.)

Janis Voigtländer

unread,
Jan 2, 2015, 7:20:44 AM1/2/15
to elm-d...@googlegroups.com
So, this commit, together with the fix from the other thread, makes sure that all pairs occurring as events on timestamp (every second) are consistent. (No guarantee for the initial value, due to the same reason as here.)

Janis Voigtländer

unread,
Jan 2, 2015, 9:22:38 AM1/2/15
to elm-d...@googlegroups.com
And then this commit ensures that in consecutive events on timestamp (fps n) the timestamps in the first component are consistent with the deltas in the second component.

Laszlo Pandy

unread,
Jan 2, 2015, 11:41:26 AM1/2/15
to elm-d...@googlegroups.com
Great work Janis. Thanks for the PR.

About the referential transparency of fps: I don't think it would be very difficult. It should be done at runtime and it doesn't require any global synchronization. All the calls to fps go to the same place and we can put a dictionary lookup there to return the old signal node instead of creating a new one. It would take a little bit more memory is the worst case and an much less if lots of the same calls are replaced with one signal node.

I am also not too concerned with the float comparison problem. The fps number should probably be rounded to the nearest millisecond because browsers don't offer more precison than that. 

Janis Voigtländer

unread,
Jan 2, 2015, 11:54:00 AM1/2/15
to elm-d...@googlegroups.com
I don't think it is so simple as it seems to you, even when done dynamically and accepting the memory overhead. Consider that there is fpsWhen (and fps is implemented in terms of fpsWhen). In fpsWhen, the decision when to set up a new timeout depends on a dynamically switching input signal. So there is no safe way to "share the signal nodes" of two calls fpsWhen 30 signal1 and fpsWhen 30 signal2, even when it happens (but maybe is not syntactically obvious) that signal1 and signal2 are semantically equivalent, and even though the two calls have the same frame rate.

Janis Voigtländer

unread,
Jan 2, 2015, 12:01:53 PM1/2/15
to elm-d...@googlegroups.com
Also, following the "handle floats by rounding the frame delay" means that in something like merge (fps 30) (fps (30+delta)) you lose any idea about whether the left signal will always cancel the right signal or not. Accepting that fps is not referentially transparent (like channel isn't), means that there is a reliable criterion: merge (fps n1) (fps n2) will never cancel any events, no matter how n1 and n2 are related, because every timer signal sets up a new event source independent of all others.

Max Goldstein

unread,
Jan 2, 2015, 2:31:42 PM1/2/15
to elm-d...@googlegroups.com
 a reliable criterion

Is the subtext that the non-referentially-transparent semantics are desirable? From a purist or theoretical perspective, this sounds like making exceptions to rules that provide Elm with its strengths. But practically, this sounds okay. The semantics of, say, overlapping multiples like 30 and 15 fps, and fpsWhen, sound difficult (not impossible) to work out and implement. But if the rules are too convoluted for the programmer and produce unexpected edge cases, we should avoid that. If I'm merging two separate timers then there's probably a reason I'm doing that. IMHO, it's more important that all the events are there rather than that they're perfectly synchronized with overlapping

Laszlo Pandy

unread,
Jan 2, 2015, 3:07:19 PM1/2/15
to elm-d...@googlegroups.com
Yes the merge issue is a problem. But did anyone explicitly decide that keeping both events is how merge and fps should work when used together? Maybe it was just an accident of implementation and this is not the most intuitive for Elm users.

Consider Keyboard.isDown. Is it also a standard library function which takes one argument and yields a new signal. Except unlike fps, merging two of the same will drop the simultaneous events:
http://share-elm.com/sprout/54a6f743e4b05f9c59b24be1

The reason is pretty obvious to me: keyboard input is a singleton, and isDown is simply a filter on it. So of course all 'a' presses come at the same time.

Now, what if we say that the most intuitive conceptual model for time in Elm is to imagine a single time source which fires every millisecond and both every and fps are just filters on the same source. fpsWhen would also be thought of as a filter which also filters based on a boolean signal.

The only reason these aren't implemented as filters now is because running a timer constantly would be very inefficient. However I am only arguing that the documentation should explain it this way to the users. Over a year ago I wrote about an idea of how to implement this efficiently, but it doesn't make such a clear mental model:
https://groups.google.com/d/msg/elm-discuss/bo0TOZ_3i6s/blMVKCq5c_AJ

On Fri, Jan 2, 2015 at 8:31 PM, Max Goldstein <maxgol...@gmail.com> wrote:
 a reliable criterion

Is the subtext that the non-referentially-transparent semantics are desirable? From a purist or theoretical perspective, this sounds like making exceptions to rules that provide Elm with its strengths. But practically, this sounds okay. The semantics of, say, overlapping multiples like 30 and 15 fps, and fpsWhen, sound difficult (not impossible) to work out and implement. But if the rules are too convoluted for the programmer and produce unexpected edge cases, we should avoid that. If I'm merging two separate timers then there's probably a reason I'm doing that. IMHO, it's more important that all the events are there rather than that they're perfectly synchronized with overlapping

--

Janis Voigtländer

unread,
Jan 2, 2015, 3:29:54 PM1/2/15
to elm-d...@googlegroups.com
Yes, I think it is preferable to accept that every, fps, and fpsWhen are not referentially transparent.

I also think that it would be prohibitively difficult and expensive to implement a referentially transparent version of fpsWhen.


2015-01-02 20:31 GMT+01:00 Max Goldstein <maxgol...@gmail.com>:
 a reliable criterion

Is the subtext that the non-referentially-transparent semantics are desirable? From a purist or theoretical perspective, this sounds like making exceptions to rules that provide Elm with its strengths. But practically, this sounds okay. The semantics of, say, overlapping multiples like 30 and 15 fps, and fpsWhen, sound difficult (not impossible) to work out and implement. But if the rules are too convoluted for the programmer and produce unexpected edge cases, we should avoid that. If I'm merging two separate timers then there's probably a reason I'm doing that. IMHO, it's more important that all the events are there rather than that they're perfectly synchronized with overlapping

--

Janis Voigtländer

unread,
Jan 2, 2015, 3:38:27 PM1/2/15
to elm-d...@googlegroups.com
Yes, a different runtime altogether could make fpsWhen referentially transparent. All I'm saying is that I don't believe a referentially transparent fpsWhen can be realistically added to the current runtime. (And the question about how merge and fps should work together is secondary to that, I think. If the timer functions are not referentially transparent anyway, then there is no ambiguity at all about what happens when one feeds signals obtained from the timer functions into merge.)

Laszlo Pandy

unread,
Jan 2, 2015, 5:31:15 PM1/2/15
to elm-d...@googlegroups.com
Fair enough. But I am still hopeful. :)

If I get any closure to figuring out how to do it, I will bring it up again. I think it would be really nice, not for referential transparency itself, but for consistency with other Elm APIs (like isDown that I mentioned).

Max Goldstein

unread,
Jan 2, 2015, 6:11:36 PM1/2/15
to elm-d...@googlegroups.com
Now, what if we say that the most intuitive conceptual model for time in Elm is to imagine a single time source which fires every millisecond and both every and fps are just filters on the same source.

I disagree. fps takes a number, possibly a Float, so some discrete underlying source doesn't make much sense. I've always thought of fps as effectful, creating a thing that exists. Therefore I think there isn't an expectation that it be referentially transparent.

Keyboard keys are mentally modeled as a listener to an event that, unlike fps, will happen whether you listen or not. If you set up two listeners, of course they're going to be identical.
Reply all
Reply to author
Forward
0 new messages