In addition to supporting Stepper a o functions, it's possible
to write "fueled" computations. A Fueled a runs some number of steps
and either isOutOfGas or yields a value of type a.
For those familiar with the functor/applicative/monad family, Fueled
is a monad, and that is the best way to interact with it.
There are two ways to run programs: all the way, via
runToCompletion, or by passing the stepper to Trampoline.init.
Fueled computationsThe repository on GitHub has more examples. The basic idiom of Fueled computations can be seen in the following function:
countDown : Int -> Fueled Int
countDown n =
if n <= 0
then return 0
else call countDown (n-1)A few notes:
Fueled functions should never take Fueled arguments. Use andThen.return.call.The countDown example is pretty silly---it always returns 0. Boring. Here's a marginally less boring example:
factorial : Int -> Fueled Int
factorial n =
if n <= 0
then return 1
else call factorial (n-1) |> andThen (\res ->
return (res * n))We can add a note:
andThen for sequencing.Here are three diverging functions, in reverse order of goodness:
bestDiverge : Int -> Fueled Int
bestDiverge n = call bestDiverge (n+1)Simple, delightful. Like Charlie on the MTA, it never returns---but it doesn't blow up the stack.
betterDiverge : Int -> Fueled Int
betterDiverge n = burn <| lazy (\() -> betterDiverge (n+1))The call function amounts to exactly this idiom.
recoveringMonadAddictDiverge : Int -> Fueled Int
recoveringMonadAddictDiverge n = burn <|
(return (n+1) |> andThen diverge)Using return into andThen is a pretty weird thing to do, but doing
it here has the same effect as using lazy.
badDiverge : Int -> Fueled Int
badDiverge n = burn (badDiverge (n+1))This function will blow up your stack---it won't actually suspend properly! Elm uses eager evaluation, so it has to compute badDiverge (n+1) before calling burn, which means computing badDiverge (n+2), and so on.
Fueled computations have a stepper that can be used with
Trampoline.init. To run a fueled computation, you might write:
main = Browser.element { init = Trampoline.init myInit Trampoline.Fueled.stepper, ... }
somewhere else, you can then run Trampoline.setInput
myFueledComputation AndGo to set the fueled computation to run and
start stepping.
Fueled computations can also be simply run... at the risk of freezing the UI thread or blocking messages. Use this function carefully.
Post-processing the results of a Fueled computation.
A fueled computation that returns immediately.
runToCompletion (return x) == x
Combining two Fueled computations applicatively.
Meant to be used with (|>), as in:
f |> Trampoline.Fueled.andThen (\x -> ...)To bind the result of f as x.
Makes a suspendable call to a Fueled computation. All recursive
calls should be written:
f n = ... call f (n-1) ...Uses up a bit of fuel.
Delays a Fueled computation.
A
Fueled ais a suspendable computation that will either diverge or eventually yield a value of typea. TheFueledtype is abstract to avoid shenanigans.