Okay, so I think that your implementation strategy is viable and addresses
the use cases that the earlier tick batching trick addressed. One could do
this as you propose with tasks, handling the “what about chained
continuations?” concern (that I had brought up) by deferring any andThened
tasks that are not Task.succeeds to later, not even attempting to include
their result messages into the current “epoch”.

I wonder how predictable this implementation would be for a user of
animations/ticks. They would have to keep a model in mind of what happens
when. The situation with non-chainable tick effects is conceptually
simpler. Or, to put it differently, maybe the actual use cases for tick
effects are all such that one only wants to “trigger something in the next
epoch directly”, and giving the user the ability to andThen on a tick may
tempt them into bad structuring of their code? Constraints in an API can be
very valuable, so if there are certain effects (ticks and possibly others)
for which better user code will result from taking the ability of
andThen-chaining
away from the user, then that would be justification for having a
separation between Task and Cmd (independently of the question of whether
andThen *can* be given some involved semantics on those effects). But I’m
not making a strong statement on that being the case here. And in any case,
you could reply that such a separation, taking andThen away from the user
for certain effects, could be put on top of an only-Tasks implementation
via a wrapper type that exposes only some of the Task operations; so it
would not require making both Task and Cmd primitive types. And you would
probably be right.
​

2016-12-13 1:54 GMT+01:00 Mark Hamburg <[email protected]>:

> More "implementation" notes for this theoretical discussion. If you
> dropped off earlier, you can stay dropped off and leave it to Janis and me.
> If you are following along, here's another little tweak:
>
> I just looked at the implementation of tasks and saw that they make very
> heavy use of andThen. They probably don't need to be implemented this
> way, but they are. So, I would simplify the logic I present above and and
> get rid of the deferred queue. When an animation frame occurs, it enqueues
> all of the active tasks for the tick and then runs the execution queue
> until it is empty before actually invoking the view function. The moment
> something triggers code that does not resolve immediately, it ceases to be
> an issue ahead of the view rendering, but until that happens we chew
> through everything that is outstanding. This could be a bit more work
> before doing the animation rendering, but it's simpler code and simpler to
> explain.
>
> Mark
>
> On Mon, Dec 12, 2016 at 6:03 AM, Mark Hamburg <[email protected]>
> wrote:
>
>> Ah. Sorry. My misunderstanding. I went looking for the Effects.tick
>> replacement in 0.17+ and only found AnimationFrame.
>>
>> Mark
>>
>> On Sun, Dec 11, 2016 at 10:32 PM Janis Voigtländer <
>> [email protected]> wrote:
>>
>>> Mark, wow, a lot of material. I’m not able to digest all of it right
>>> now. But something quick I want to say, because I have the impression that
>>> there is a misunderstanding about the current state of tick batching
>>> support.
>>>
>>>
>>> This:
>>>
>>>
>>>
>>>
>>> Since I wasn’t proposing to get rid of subscriptions and since this
>>> functionality is covered through subscriptions, my proposal arguably
>>> doesn’t cause a problem.
>>>
>>>
>>>
>>>
>>> baffles me. I’m not sure what you mean by “this functionality is covered
>>> through subscriptions”. Tick batching wasn’t covered through signals (the
>>> closest predecessors of subscriptions), and isn’t covered through
>>> subscriptions (or at all!) right now.
>>>
>>>
>>> Specifically and relatedly, concerning this:
>>>
>>>
>>>
>>>
>>> The special behavior for tick wasn’t particularly documented for the
>>> 0.16 Effects module and isn’t really documented for the 0.17+
>>> AnimationFrame module.
>>>
>>>
>>>
>>>
>>> I should clarify that the tick batching trick as existed in the 0.16
>>> Effects module is *not implemented* for Elm 0.17+. Apparently since
>>> gamey stuff is not anymore in Elm’s focus, Evan didn’t bring the “first
>>> ever effect manager” over to the new world when effect managers as such
>>> became a thing.
>>>
>>>
>>> (Did I indicate previously that tick batching as existed before is a
>>> thing currently in core or other 0.17/0.18 packages? I don’t think so.
>>> I was only bringing it up as a thing that can be done with effect managers
>>> and benefits from, maybe even is absolutely dependent on, having separate
>>> concepts of Task and Cmd. To decide about maintaining that latter
>>> stance, will require digesting your material more fully.)
>>>
>>>
>>> ​
>>>
>>> 2016-12-11 21:26 GMT+01:00 Mark Hamburg <[email protected]>:
>>>
>>> Since I wasn't proposing to get rid of subscriptions and since this
>>> functionality is covered through subscriptions, my proposal arguably
>>> doesn't cause a problem. But that's just getting through the loophole of
>>> this specific example to dodge this particular case, so let me see if I can
>>> get straight what the special property is that we need to preserve if we
>>> were still trying to implement a tick command in the same manner of the
>>> 0.16 tick effect. (It's worth noting as a side note that in 0.16, all
>>> effects other than tick were tasks, so obviously tasks are close to
>>> covering the role of commands — née effects — but tick may point to extra
>>> wrinkles.) I'm not going to assume a constraint by the current task
>>> implementation, but any revised implementation has to have a clear behavior
>>> in conjunction with the existing task APIs since its those APIs that make
>>> tasks an attractive alternative to commands.
>>>
>>> The special behavior for tick wasn't particularly documented for the
>>> 0.16 Effects module and isn't really documented for the 0.17+
>>> AnimationFrame module. Am I correct that the concern is that when the
>>> animation frame "arrives":
>>>
>>>
>>>    1. We run all of the update functions before actually calling the
>>>    view function; and
>>>    2. Any tick effects that result from those updates will be batched
>>>    together for the next animation frame
>>>
>>> Was there anything else that I'm missing?
>>>
>>> The first point means that any implementation is going to have to be
>>> built in conjunction with the Elm runtime since coordinating with the call
>>> of the view function requires knowledge of when and how the view function
>>> gets run. But since the whole point of this is to coordinate with the view
>>> function that shouldn't be surprising nor is it particularly odd. It does
>>> mean that my simple example using port tasks wouldn't work because it
>>> wouldn't enjoy that coordination, but extra requirements add extra
>>> implementation details.
>>>
>>> So, what we want is that we can divide time up with a series of
>>> animation frame events and at each animation frame event, we want to start
>>> a new epoch for collecting tick effects, run the updates driven by all
>>> effects collected for the previous epoch, and then do the view rendering. A
>>> naive implementation will just keep listening for all animation frames. A
>>> more sophisticated implementation will take steps to shutdown that
>>> observation when it is no longer necessary.
>>>
>>> The vague handwaving in the above is seemingly around what it means to
>>> say "run the updates driven by all effects collected for the previous
>>> epoch". In particular, how does this interact with task chains built using
>>> andThen, etc.? I am going to make a distinction between two ways to
>>> specify successor computation on a task. In one case with map the results
>>> or the error coming from the task to produce a new result or error. In the
>>> other, we map the results or the error coming from the task to produce a
>>> new task. I would argue that the first category is what leads to actual
>>> messages for the model. The second can be viewed as making no changes to
>>> the model but queueing new task executions.
>>>
>>> So, how could this work in a task-based scenario? I'm going to speak of
>>> tasks initiating and resolving to distinguish between when they have code
>>> invoked v when they deliver values.
>>>
>>> When a tick task initiates, it adds itself to the set of tick task
>>> executions for the current tick epoch.
>>>
>>> When we receive an animation frame call, we do the following:
>>>
>>>    1. We grab the contents of the tick epoch set and reset the set to
>>>    empty
>>>    2. For each of the tick executions in the tick epoch set grabbed in
>>>    (1), we resolve the execution using the current time. This means running
>>>    down the network of tasks chained onto the executions. For cases where we
>>>    map the result or error to a new result or error, we do so and keep 
>>> working
>>>    down the chain. For cases where we map the result or error to a new task,
>>>    we queue that task for execution.
>>>    3. We execute the view function.
>>>
>>> In this way, we deliver all of the updates that are simply dependent on
>>> the tick event or a mapping thereof while initiating any further
>>> computations that were waiting for the tick event.
>>>
>>> The other piece of concern is how task queueing works and when tasks get
>>> to execute. I would probably go for a structure on the task queue in which
>>> it is also divided into epochs. We process all of the tasks within an
>>> epoch. Any tasks produced during this processing as a result of andThen,
>>> etc. go in the next epoch. All tasks produced during an update go in the
>>> current epoch which we process at the end of the update. This design keeps
>>> chains of tasks from blocking other update processing. We could go further
>>> and pull tasks generated by tasks off of a queue that executes even more
>>> incrementally. The key point is that all of the tasks from an update get
>>> initiated immediately following the update and tasks initiated from other
>>> tasks happen as we have time to process them.
>>>
>>> In the case of ticks, this means that a tick task that initiates a chain
>>> will execute immediately following the update but a tick task that is
>>> initiated from some other task might get run at an arbitrary later time.
>>> (How arbitrary determines whether we can do things like write Task.tick
>>> |> Task.andThen (always Task.tick) in order to wait two ticks as
>>> opposed to at least two ticks.)
>>>
>>> To make animation frame updates as efficient as possible, we probably
>>> also want to avoid draining the task-initiated task queue until after the
>>> view function is run. That's not critical to the semantics but it could
>>> matter for performance.
>>>
>>> Now, maybe I've missed some other detail that matters, but as I've said
>>> the documentation for both Effects.tick and AnimationFrame is
>>> relatively thin on detailed semantics and requirements. But if I've gotten
>>> the concerns right, then I believe the above shows how a task-based system
>>> could do what is called for here. Am I missing something? I know I'm
>>> handwaving through some of the execution machinery, but my intuition from
>>> writing lots of promise systems in Lua says that this sort of structure
>>> would work.
>>>
>>> Mark
>>>
>>> On Sat, Dec 10, 2016 at 10:20 PM, Janis Voigtländer <
>>> [email protected]> wrote:
>>>
>>>
>>>
>>> In the case of ticks, what I gather from a read through of the code that
>>> it does is guarantee that all of the tick requests placed between animation
>>> frame strobes will all be delivered at the next animation frame strobe. Is
>>> that a correct read?
>>>
>>>
>>>
>>>
>>> Yes, that is a correct read. But no, I don’t think that your post shows
>>> the same can be done with tasks instead of commands. The reason is, as
>>> previously mentioned, the existence of andThen for tasks. And your
>>> “P.S.” does not address this, because you are not there considering what
>>> the real complication with andThen is. The complication is not whether
>>> a tick task involved in two andThen chains is run once or twice, the
>>> complication is how to deal with the “continuations” in those two chains.
>>> Let’s look at this with some example:
>>>
>>>
>>> In the Effects/Cmd world, using ticks would be like this:
>>>
>>>
>>>
>>>
>>>
>>>    1. At some point, a command tick tagger1 reaches the runtime system,
>>>    where tagger1 : Time -> Msg for Msg being the program’s message
>>>    type. The runtime system will not do anything at that point, except for
>>>    registering in some internal state that a tick request was issued, and 
>>> that
>>>    the tagger to use for it is tagger1. So essentially, the runtime
>>>    system (specifically, the effect manager) at that point stores the 
>>> function
>>>    tagger1 in some list.
>>>
>>>    2. At some point after that, but before the next animation frame
>>>    happens, a command tick tagger2 reaches the runtime system. At that
>>>    point, the effect manager adds the function tagger2 to said internal
>>>    list.
>>>
>>>    3. Some time later, the next animation frame is due. So the runtime
>>>    system looks at its internal list, sees that there are [tagger1,
>>>    tagger2], takes the current time stamp t, evaluates msg1 = tagger1 t
>>>    and msg2 = tagger2 t, and passes msg1 and msg2 to the program’s
>>>    update function one after the other *but without any intermediate
>>>    view rendering*. If the update function creates additional
>>>    effects/commands, the ones created from the calls of update with msg1
>>>    and msg2 are batched (so that actually the grouping together of
>>>    updates will propagate to the future).
>>>
>>>
>>>
>>>
>>> What about in your hypothetical world in which no Cmd abstraction
>>> exists, but instead tick has type Task Never Time? Now instead of just
>>> using tick with functions of type Time -> Msg, tick can be used in task
>>> chains, like tick |> andThen cont with cont : Time -> Task Never Msg.
>>> Let’s look at such a scenario:
>>>
>>>
>>>
>>>
>>>
>>>    1. At some point, a task tick |> andThen cont1 reaches the runtime
>>>    system, where cont1 : Time -> Task Never Msg. As above, the runtime
>>>    system shouldn’t do anything at that point, except for registering in its
>>>    internal state that there is this tick request and what to do when it
>>>    eventually becomes active. The difference to above is that now instead of
>>>    storing a function Time -> Msg for later use, the system has to
>>>    store a function Time -> Task Never Msg. Fine enough.
>>>
>>>    2. At some point after that, but before the next animation frame
>>>    happens, a task tick |> andThen cont2 reaches the runtime system,
>>>    where cont2 : Time -> Task Never Msg. Again, the effect manager
>>>    should simply add that to the internal list of open tick requests.
>>>
>>>    3. Some time later, the next animation frame is due. Now what? The
>>>    runtime system knows that two tick requests are open. So of course it 
>>> takes
>>>    the current time stamp t and passes it to the two functions stored
>>>    in its internal list. But instead of getting some msg1 : Msg and msg2
>>>    : Msg as above, it now gets some task1 = cont1 t : Task Never Msg
>>>    and task2 = cont2 t : Task Never Msg. So *unlike above*, the
>>>    strategy now cannot be to pass the relevant two messages to the program’s
>>>    update function while ensuring that no view rendering happens in
>>>    between (which was the whole point of tick batching in the animation
>>>    scenario). Because those messages are simply not available right now. 
>>> What
>>>    is available are two tasks that when run will eventually return with
>>>    messages (maybe after some http requests or whatever). So the runtime
>>>    system can now fire off task1 and task2, but there is no way to tell
>>>    when they will return, and certainly no assumption can be made that they
>>>    will return still before the view rendering that is supposed to happen
>>>    right now because we have an animation frame just due. In consequence, 
>>> the
>>>    whole point of tick batching is lost. We don’t get to ensure that groups 
>>> of
>>>    update calls get performed “atomically” without intermediate view
>>>    rendering.
>>>
>>>
>>>
>>>
>>> What do you think about the above? To me, it means that in the “just
>>> tasks” world tick batching as in the “separate tasks and commands” world is
>>> not possible (with the same quality).
>>>
>>>
>>> ​
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>> --
>>>
>>>
>>> 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 [email protected].
>>>
>>>
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>> --
>>>
>>>
>>> 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 [email protected].
>>>
>>>
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>>
>>> --
>>>
>>>
>>> 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 [email protected].
>>>
>>>
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>>
>>>
> --
> 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 [email protected].
> For more options, visit https://groups.google.com/d/optout.
>

-- 
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 [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to