std.fuel
Fuel: a computation run on so much of it, stopped where it runs out —
a watchdog, killing a runaway computation, not a time slice. Import it
by a name, import std.fuel as Fuel, its effect in the program’s world.
import coimport std.fuel as Fuelimport std.io
spin : Int -> Intspin n = spin (n + 1)
main : Co (stdio <+> Fuel.fuel) Unitmain = do r <- Fuel.limited 100000 (later (\u -> spin 0)) print (debug r)Built with fuel — fuel = True in its build.hf, --fuel to heff compile, run, test or bench
— a program is ticked by the compiler: a decrement and a branch at the
entry of each function that may loop (one calling a function of its own
recursive group, or a closure); a loop calling nothing in its rounds
(a tight one) is ticked once a batch of them, charged the batch, as
many rounds as make a batch of some sixty-four steps of the compiler’s
— at most batchMost. So limited n c stops any loop of c, one
doing no operation too, near its nth tick: a batched loop at most a
batch before it, charged a batch more at most each time it is entered
(the program above says Nothing). Without it, nothing is ticked but
the operations of the world, so limited stops c only at one: a pure
loop with none runs past its limit, to its end or for ever, and heff
warns where limited is used.
The ticks are an abstraction leak there is no way around: pure code carries no effect, so the compiler cannot tell which code runs under a limit, and every loop of a program built with fuel pays — a tight loop’s branch keeps the C compiler from unrolling it, its rounds four to six times the instructions (see the reference’s Fuel). So a program says it is built with fuel, and only one that limits pure work should.
A limit within a limit gets at most what is left of the outer, and
what it uses is the outer’s: where the inner runs out of its own, it is
Nothing and the outer goes on; where it runs out of what was left of
the outer, the outer is Nothing too. A failure inside is handed on
as it came. The count is the domain’s (a thread of the system’s): a
domain spawned inside runs with no limit. What the stopped computation
had not done is left undone, as a failure leaves it: a with’s release
it skips is not run, the thing let go of by its finalizer where its
last reference is dropped — what only the stopped frames held, on C,
leaked.
The C backend’s (fuel.provider.c): c run under heff_try, the
runtime’s count set, a stop jumped out of. JavaScript’s
(fuel.provider.js): thrown out of, the limit the computation’s across
an operation that finishes later and the turns of other domains.
fuel
fuel : HFunctorFor a computation run on so much fuel.
batchMost
batchMost : Intthe most rounds of a loop charged at once, a batch’s: a limit stops a batched loop no more than these many rounds before it, and charges no more than these more each time it is entered (the compiler’s own)
limited
limited : {e : HFunctor} -> {a : Type} -> {{fl : fuel <: e}} -> Int -> Co e a -> Co e (Maybe a)c run here on at most n of fuel: Just its answer, or Nothing
where it runs out first, the rest of it not run. The fuel is the
program’s ticks where it is built with fuel, else the world’s
operations alone (see above).