Open turion opened 3 months ago
The extra m action can be used to initialise a resource.
That's actually the wrong approach: The clock value should already contain the initialised resource! So if anything, there should be an action m cl
that initialises the resource, and then the clock value can be used. Although that will still have the same problem: The Rhine
is then not statically known.
There is a big semantic change that I'm unsure about. On master
the first tick is always a certain time after the initial time. Also, the duration sinceLast
on the first tick is the time from the initial time the first tick. With this change here, we would have the awkward situation that sinceLast
on the first tick is always 0! This is especially awkward for a fixed rate clock where we expect sinceLast
to be largely constant. Instead, it is now 0 and then afterwards largely constant.
I'm not sure how to proceed then. I see several different stances:
class Clock m cl where
initialTime :: cl -> m (Time cl)
initClock :: cl -> Automaton m () (Time cl, Tag cl)
or even
class Clock m cl where
initialTime :: cl -> m (Time cl)
initClock :: Automaton m cl (Time cl, Tag cl)
In latter case, the running clock is completely known statically, which is great.
sinceInit
and sinceLast
have no meaning and could have any values, e.g. 0 or 'Nothing'. This may be right, but is unhelpful in many cases and breaks with the established semantics. On the other hand, for CSV clocks (#226) and similar batch processing scenarios, there is no sensible start time other than a manually supplied one or the first timestamp.In latter case, the running clock is completely known statically, which is great.
But this also means that many clocks like RescaledClock
or SelectClock
cannot be implemented anymore.
Since long, I wanted to simplify the type signature of
initClock
fromto
There are several reasons for the more complicated type signature:
m
action can be used to initialise a resource. (Although this is usually the least concern, the initialization could also happen upon the first tick, or be done with aReaderT
newtype.)m
action is used to generate an initial time. This has several advantages:Maybe
)But there are also downsides to this complicated type signature:
initClock
directly returns a running clock, we could simply merge the timestamp streams.With #299 (automata) there would be a new downside: The state of the running clock is typically not known statically (because it is hidden behind a monadic action), therefore GHC cannot optimize the whole
Rhine
further after clock erasure. This causes a performance degradation that is not easily justifiable.Overall I believe that after #299, a serious attempt at simplification should be made.