Module Miou.State

An introduction about Miou's internals.

This module proposes a re-interpretation of the effects management offered by OCaml 5 centered on the idea necessary in the implementation of a round-robin scheduler. For simplicity, as soon as we talk about scheduler, we will talk about round-robin scheduler. This one focuses on:

In this sense, the scheduler needs basic operations in the manipulation of tasks. Tasks are the functions (in the OCaml sense) to be executed. The scheduler must be able to:

  1. launch a task / apply a function
  2. stop a task at a specific point according to a metric
  3. have a representation of this suspended task (a state) and be able to store it
  4. restart a task from its state

Usually, the metric used to "stop" a task is time. That is to say that we could stop a task after 100ms has elapsed for example. Unfortunately, it seems difficult to translate this into OCaml. In this sense, the choice of our metric is: effect. That is to say that a task can only emit quanta effects (which we will of course have to manage). The quanta is the number of effects the task can produce.

Note about the Effect module.

If you follow us well and you are already familiar with the effects module in OCaml, you suspect that our implementation currently uses the Effect.Shallow module which allows you to manage 1 effect (and only one).

It is allowed to use the Effect.Deep module for the implementation of a scheduler - the latter makes it possible to manage several effects until a certain state of the function is obtained. In this case, when using the Effect.Deep module, we spontaneously obtain 2 states:

Note, however, the subtlety of the first in relation to what we want to "observe" of a task for our scheduler. This case explains the fact that the task stopped on a blocking event. We consider that this subtlety discriminates the tasks (which is in opposition to our communist ideal) between those which block and those which do not block.

In this sense, in our ideal and according to what is required by a round-robin scheduler, the suspension mechanism (stop a task) intervenes systematically for each effect. Discrimination must be radically combated.

The goal of this interface.

This module therefore allows us to "drive" our development on these principles described above - indeed, the Effect module is perhaps a little too permissive. Thus, if you have to modify this module and specifically this interface, beware of a "balkanization" effect which could betray our ideals.

type ('a, 'b) continuation

The type of continuations. ('a, 'b) continuation is the state of a function _ -> 'b. 'a is the type of the value to continue the continuation. The user can also discontinue with an exception the continuation.

type 'a t = private
  1. | Finished of ('a, exn) Stdlib.result
  2. | Suspended : ('a, 'b) continuation * 'a Stdlib.Effect.t -> 'b t
  3. | Unhandled : ('a, 'b) continuation * 'a -> 'b t

The type of function states.

The state of a function is its execution state. A function can finish with its return value or an exception, or it can suspend on an Effect.t. In the case of a suspension, the user can "continue" the execution via what is expected by the function depending on the effect and the function once. Note that once can only be used once on a given value (otherwise, an exception Continuation_already_resumed is raised by OCaml).

module Op : sig ... end
type perform = {
  1. perform : 'a 'b. ('a Op.t -> 'b t) -> 'a Stdlib.Effect.t -> 'b t;

Type of the effect handler.

perform is a function which should handle incoming effects and give an operation Op.t via the given conitnuation k.

val make : ('a -> 'b) -> 'a -> 'b t

make fn value makes a new function state by executing the function with the given argument.

val suspended_with : ('c, 'a) continuation -> 'c Stdlib.Effect.t -> 'a t

suspended_with k eff allows you to create a state from the given suspension k and the effect eff which produced the given suspension.

val once : perform:perform -> 'a t -> 'a t

once ~perform state applies perform once on the given state if the latter emits an effect.

val fail : exn:exn -> 'a t -> 'a t

fail ~exn state discontinue the given state with the given exception. It always return Finished (Error exn).

val pure : ('a, exn) Stdlib.result -> 'a t

pure value returns Finished value.

val run : quanta:int -> perform:perform -> 'a t -> 'a t

run ~quanta ~perform state applies once quanta times. If perform responds with Op.interrupt (and therefore does nothing), even though there may be a few quanta left, the function returns the last state obtained.

The same applies to yield, except that the continuation has burnt itself out. In other words, yield is equivalent to send (); interrupt but costs only one quanta.