On Monday, December 12, 2016 at 11:56:19 PM UTC-6, Janis Voigtländer wrote:
>
> 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”.
>
+1 This is a cleaner and simpler model (a related question from a while ago 
<https://groups.google.com/d/msg/elm-discuss/AF1U1FqVjJc/CKxDOKWzEAAJ>). 

+1 for reducing Cmd and Task to a single primitive concept if that is 
doable.

Not sure if others have seen this, but I really like the design of Effects 
in Python <https://effect.readthedocs.io/en/latest/>, including chaining, 
effect managers (dispatchers + performers), and opaque vs. transparent 
types.

 

> 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] <javascript:>>:
>
>> 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] 
>> <javascript:>> 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] <javascript:>> 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] 
>>>> <javascript:>>:
>>>>
>>>>> 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] <javascript:>> 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] <javascript:>.
>>>>>>
>>>>>>
>>>>>> 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] <javascript:>.
>>>>>
>>>>>
>>>>> 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] <javascript:>.
>>>>
>>>>
>>>> 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] <javascript:>.
>> 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