Module Fexpr_prim_descr.Describe
All the necessary smart constructors to describe the structure of a fexpr primitive and its conversion from/to flambda.
A primitive is necessarily described with its arity with the nullary to variadic constructors. It is given the syntactic name of the primitive (with the %), and parameters. It returns the conversion function from the flambda side, and register internally the reverse conversion that can be looked-up with lookup_prim.
The complexity comes from the description of the parameters of the primitive variant itself, which can be arbitrary caml values.
As param shows, there is two kinds of primitive parameter:
- Tuples, written
.[val1, ..., valN] - Labeled, written
.label[val1, ..., valN]. They can be without arguments.labelto denote flags.
Flags and labeled can appear in any order, at any positions. Both of them can be nested, the dot separator is only for toplevel parameters.
The type value_lens is used to describe conversions of custom types. Label, flags and inner values are all treated the same.
The constructors provide composable param_cons values. They allow the construction of more complex types, such as tuples, list, options or boolean.
Primitive descriptors
val todo : string -> 'p convLeaves the primitive unimplemented, will raise error if encountered during conversion
val nullary : string -> params:'p param_cons -> 'p cons0 -> 'p convFor Flambda_primitive.nullary_primitive. Takes ~params constructor and final primitive variant wraping function.
val unary : string -> params:'p param_cons -> 'p cons1 -> 'p convFor Flambda_primitive.unary_primitive. Takes ~params constructor and final primitive variant wraping function.
val binary : string -> params:'p param_cons -> 'p cons2 -> 'p convFor Flambda_primitive.binary_primitive. Takes ~params constructor and final primitive variant wraping function.
val ternary : string -> params:'p param_cons -> 'p cons3 -> 'p convFor Flambda_primitive.ternary_primitive. Takes ~params constructor and final primitive variant wraping function.
val quaternary : string -> params:'p param_cons -> 'p cons4 -> 'p convFor Flambda_primitive.quaternary_primitive. Takes ~params constructor and final primitive variant wraping function.
val variadic : string -> params:'p param_cons -> 'p consN -> 'p convFor Flambda_primitive.variadic_primitive. Takes ~params constructor and final primitive variant wraping function.
Parameter descriptors
val todop : string -> 'p param_consSame as todo for specific parameter. Can be buried in some specific cases.
val value : 'a value_lens -> 'a param_consConstructor from simple lens
val positional : 'a param_cons -> 'a param_consSyntactic positional parameter
val labeled : string -> 'a param_cons -> 'a param_consSyntactic labeled parameter
val flag : string -> unit param_consSyntactic flag parameter. Flags have no values and the only information they carry is their presence. flag has little reason to appear by itself except for pattern description. It will mostly appear within constructor_flag.
val bool_flag : string -> bool param_consSame as flag but always matches and tell if the flag has been matched or not.
val constructor_flag :
?no_match_handler:('p -> unit) ->
(string * 'p) list ->
'p param_consTakes an associative list of flags and corresponding value. With optional handling when encountering an unlisted value.
Caution: Implementing types that way do not ensure exhaustivity unfortunately. The no match handler can be used to enforce it by matching on its argument.
val param0 : unit param_consState no parameters are expected. Only useful at toplevel.
val param2 : 'a param_cons -> 'b param_cons -> ('a * 'b) param_consCouple of constructions.
val param3 :
'a param_cons ->
'b param_cons ->
'c param_cons ->
('a * 'b * 'c) param_consTriple of constructions.
val param4 :
'a param_cons ->
'b param_cons ->
'c param_cons ->
'd param_cons ->
('a * 'b * 'c * 'd) param_consQuadruple of constructions.
val param5 :
'a param_cons ->
'b param_cons ->
'c param_cons ->
'd param_cons ->
'e param_cons ->
('a * 'b * 'c * 'd * 'e) param_consQuintuple of constructions.
val default :
def:'p ->
?eq:('p -> 'p -> bool) ->
'p param_cons ->
'p param_consSpecify a default value for the underlying constructor, which will be used if there is no match. In conversion to fexpr, if the provided parameter equals the default, no parameter will be produced. ~eq allows the optional definition of equality function, defaults to Stdlib.((=)).
val option : 'p param_cons -> 'p option param_consMakes a construction optional. Always matches.
val list : 'p param_cons -> 'p list param_consMakes a list of a construction. Atomized to make the list explicit, expects its own level of bracket syntactically
val maps :
to_:(encode_env -> 'b -> 'a) ->
from:(decode_env -> 'a -> 'b) ->
'a param_cons ->
'b param_consCustom transformation of parameter value.
Labels inverted to be related to the argument. maps x ~to_ ~from read "maps x to return type" and "maps x from return type"
Pattern maching
Using monadic notation, we define patterns using let| binding a param_cons and a boxing function, ending with a call to return_either with a full pattern matching on the outer type. This is then bound to the resulting param_cons with let|=. The order of declaration of pattern is the order in which they will be tried against. Example:
let|= example =
let| b = bool, (fun _ b -> if b then 1 else 0) in
let| i = int, (fun _ -> Fun.id) in
return_either
(fun env p ->
match p with
| 0 -> b env false
| 1 -> b env true
| x -> i env x)This is a int param_cons encoded to either a bool "true" | "false" or an int.
type 'p encode_case = encode_env -> 'p -> param listval return_either : (encode_env -> 'p -> param list) -> 'p build_eitherval (let|) :
('case param_cons * (decode_env -> 'case -> 'p)) ->
('case encode_case -> 'p build_either) ->
'p build_eitherval (let|=) : 'p build_either -> ('p param_cons -> 'a) -> 'aval param2_case :
decode:(decode_env -> 'c1 -> 'c2 -> 'p) ->
'c1 param_cons ->
'c2 param_cons ->
('c1 * 'c2) param_cons * (decode_env -> ('c1 * 'c2) -> 'p)val param3_case :
decode:(decode_env -> 'c1 -> 'c2 -> 'c3 -> 'p) ->
'c1 param_cons ->
'c2 param_cons ->
'c3 param_cons ->
('c1 * 'c2 * 'c3) param_cons * (decode_env -> ('c1 * 'c2 * 'c3) -> 'p)val param4_case :
decode:(decode_env -> 'c1 -> 'c2 -> 'c3 -> 'c4 -> 'p) ->
'c1 param_cons ->
'c2 param_cons ->
'c3 param_cons ->
'c4 param_cons ->
('c1 * 'c2 * 'c3 * 'c4) param_cons
* (decode_env ->
('c1 * 'c2 * 'c3 * 'c4) ->
'p)val param5_case :
decode:(decode_env -> 'c1 -> 'c2 -> 'c3 -> 'c4 -> 'c5 -> 'p) ->
'c1 param_cons ->
'c2 param_cons ->
'c3 param_cons ->
'c4 param_cons ->
'c5 param_cons ->
('c1 * 'c2 * 'c3 * 'c4 * 'c5) param_cons
* (decode_env ->
('c1 * 'c2 * 'c3 * 'c4 * 'c5) ->
'p)Parameter payload descriptors
Can be extended here. Or defined elsewhere for specific values.
val string : string Fexpr.located param_consRaw value
val int : int param_consParsed as integer. Fails if the string is not one.
val bool : bool param_consParsed as boolean. Fails if the string is not one.
val diy : string Fexpr.located param_consSame as string. With some tweaks to the parser, we could call actual parsing start-points in it.