The purpose of this module is to expose the "guts" of the logic so that it can be tested. It is not in the "exposed-modules" of the elm.json file, so it's not directly accessible to clients of the package.
Testing just the things exposed to clients of the package would be awkward, since it would require asynchronous tests. Here, we don't do anything actually asynchronous:
You provide the Time with all messages (so it's just data).
When we want to schedule a Check, we just return a List Time ... it's
the caller's job to actually schedule it.
The user-facing documentation for these types is in Debouncer.Basic and
Debouncer.Messages, since those are the exposed modules.
An opaque type representing the configuration for a debouncer.
We provide various "constructor" functions (rather than exposing the type to clients) so that we can change the type in future without a major version bump.
We require a Config that is separate from the Debouncer itself,
because changes to the configuration once the debouncer is actually
operating would need to be handled specially, via messages.
Each of the items is a Maybe, since you don't necessarily want to
automatically emit at all for one kind of event or another.
If all three are Nothing, then the debouncer will never automatically
emit -- you would have to tell it to emit at some point, based on some
external event.
Our basic type. Pairs a configuration with some state that changes as inputs are received and outputs are provided.
A useful reminder of what our intervals mean.
Docs docs docs.
Docs docs docs.
output needs to be a Maybe because we may have emitted while
unsettled, due to emitWhileUnsettled. And, we could arrive at
settleWhenQuietFor without any further input, so we don't
necessarily emit anything when we become settled.
Note that this changes the type of the Config to match the
type of the acummulator provided.
Accumulates by taking a sum of the inputs.
Accumulates all inputs in a list.
Note that the output list will have the most recent input first.
Accumulates by appending the input to the output.
Accumulates by appending the output to the input.
Cancellation is one way we can directly modify a debouncer. Basically, we
just forget our state. There may be future Check ... messages coming, but
they will consider what to do based solely on our state at that time. So,
forgetting our state is sufficient to "cancel" any pending output.
A starting point for a configuration which debounces -- that is, which will emit once quiet for the time you specify.
So, debounce (2 * Time.second) is equivalent to
manual
|> settleWhenQuietFor (Just (2 * Time.second))If you also want to emit using the first input, then you can use
emitWhenSettled. For instance, the following configuration would
emit the first input immediately when becoming unsettled, and then
emit any subsequent input once the debouncer was quiet for 2 seconds.
debounce (2 * Time.second)
|> emitWhenUnsettled (Just 0)Modify a Config by controlling whether to emit the first
input when becoming unsettled.
emitFirstInput True config is equivalent to
emitWhenUnsettled (Just 0) configemitFirstInput False config is equivalent to
emitWhenUnsettled Nothing configFor more complex cases, where you want to emit the first input on
a different interval than others, but not immediately, you can
use emitWhenSettled directly.
Docs docs docs.
Docs docs docs.
Accumulates by just keeping the first input.
A convenience when you'd rather think in seconds than milliseconds.
fromSeconds 0.5 --> 500Accumulates by just keeping the last input.
A starting point for a "manual" configuration.
By default, it:
So, without more, you would need to tell this debouncer when to emit something -- it would never happen automatically.
To change any of those parameters, use the various functions that alter a
Config (i.e. settleWhenQuietFor, emitWhenUnsettled, emitWhileUnsettled).
By default, the output type is the same as the input type. However, you can
change that by using the accumulateWith function to provide a different
accumulator.
Docs docs docs.
Sanitize the config to simplify some of the logic.
Docs docs docs.
A starting point for a configuration which throttles -- that is, which will emit the first input immediately, and then accmulate and emit no more often than the specified interval.
So, throttle (2 * Time.second) is equivalent to
manual
|> emitWhileUnsettled (Just (2 * Time.second))
|> emitWhenUnsettled (Just 0)Docs docs docs.
The second return parameter is not a Cmd, but instead a list of intervals
at which we ought to check whether to emit something. This assists with
testability (since we can test whether that list is correct). The caller is
responsible for actually turning that list into commands.
A type which represents a function which incorporates an input into the output so far.