The Trampoline library is for writing suspendable computations, i.e., long-running computations that don't freeze the UI. The library supports two interfaces: step functions and fueled computations. The implementation is entirely based around step functions.
Long running computations have two parts: a Stepper
and an
input. You'll set the stepper when configuring the trampoline state
(see init
).
a -> StepResult a o
A Stepper a o
is a function that takes a value of type a
and either:
a
(which
should be stepped more)o
.For example, a stepper for a programming language might look like:
type alias ExprStepper = Stepper Expr (Result RuntimeError Value)
The types a
and o
need not be different. A long running
computation which always succeeds and produces an integer might use a
stepper like:
type alias IntStepper = Stepper Int Int
The result of a single step: either another a
, or we're done
with an o
.
Your program will control the steppers' inputs and when it runs by sending messages. Internally, long running computations use private messages to continue running while allowing other messages to be processed.
The messages used by Trampoline's stepper. The actual definition
is abstract, but setInput
, go
, stop
, and msg
should suffice.
setInput : a -> AndGo -> Msg a msg
Sets the input for the stepper function. You can set the input and start by running:
setInput 5 AndGo
Or you can wait for a later go
message by running:
setInput 5 AndWait
Determines whether to immediately start running after SetInput
or to wait for Go
message.
go : Msg a msg
Tells the stepper to go. It's harmless to send this message when there's no input, the stepper is already running, or the stepper is stopped.
stop : Msg a msg
Tells the stepper to step. It's harmless to send this message when there's no input or the stepper is already stopped.
msg : msg -> Msg a msg
Turns a message from your application into a Msg a msg
.
The current state of the stepper. This information is revealed completely so you can reflect it best in your program; you should probably not change it yourself.
{ numSteps : Internal.Gas
, totalSteps : Internal.Gas
}
The stepper keeps some rudimentary statistics. Eventually, this information (along with some timing information) will allow for the stepper to adaptively determine how many steps to take at a time. As such, while it's currently harmless to change this information, it's inadvisable.
{ state : State a o
, stats : Stats
, stepper : Stepper a o
, model : model
}
A Model a o model
wraps your program's model
type to
accommodate an Stepper a o
.
A variety of wrappers will help you manage state and handle only the messages your program needs to know about.
init : (flags -> ( model, Platform.Cmd.Cmd (Msg a msg) )) -> Stepper a o -> flags -> ( Model a o model, Platform.Cmd.Cmd (Msg a msg) )
The init
wrapper initializes the outer trampoline state given:
flags -> (model, Cmd (Msg
a o msg))
Stepper a o
for the long running computationThis is meant to be used with Browser.element, e.g.,
Browser.element
{ init = Trampoline.init myInit myStepper
, ... }
See update
and subscriptions
, too.
update : (msg -> model -> ( model, Platform.Cmd.Cmd (Msg a msg) )) -> (o -> msg) -> Msg a msg -> Model a o model -> ( Model a o model, Platform.Cmd.Cmd (Msg a msg) )
A wrapper for your program's update
function. You must provide:
(msg -> model -> (model, Cmd (Msg a msg)))
o -> msg
This is meant to be used with Browser.element, e.g.,
Browser.element
{ update = Trampoline.init myUpdate NotifyMsg
, ... }
where NotifyMsg
is some message defined in your application of type o -> msg
.
subscriptions : (model -> Platform.Sub.Sub msg) -> Model a o model -> Platform.Sub.Sub (Msg a msg)
A wrapper for your program's subscriptions
function, which amounts to running Platform.Sub.map
.