Up
# module Type_generic

: sig

A computation is the type of an operation that can be applied to various different kind of types. It is expressed as a type with one parameter:

type 'a computation

Examples of computation:

type sexp_of_t = ('a -> Sexp.t) computation type type_struct = Type_struct.t computation

The term `generic`

is used to refer to a specific implementation of a computation
whose concrete implementation is programmed using the type representation of values.

For example, when one uses `with sexp`

as a way to implement the `sexp_of_t`

computation, the technique used is code generation at compile time. Another approach
is to define a generic function `sexp_of_t`

that inspects the representation of the
type at runtime.

This module offers an abstraction over type rep in order to implement generics in a efficient way.

Provided from a user enough pieces of implementation regarding a particular computation, this module returns essentially the following function:

(** main function : get the computation from the typerep *)
val of_typerep : 'a Typerep.t -> ``generic of 'a computation`

that allows one to get the generic computation operating on a given type `'a`

.

#

module Variant_and_record_intf : module type of Variant_and_record_intf

#

module type Named = sig

#

type 'a computation

#

module Context : sig

Work in progress representation of a computation. This is mostly used to handle
recursive types. While building a computation on a recursive type, one needs to have
some computation available for the location where the type appears recursively.
`init`

will be called once on each new type_name met during the traversal of a type.
Each time the same type is encountered again, `get_wip_computation`

will be called.
At the end of the traversal of that particular type, `set_final_computation`

will be
called, offering as a way to "close" the wip representation. `'a t`

can be mutable
(and is likely to be in practice).

After a `set_final_computation`

is performed and return a final computation C for a
type_name, C will be memoized and returned for each further occurrences of the same
type_name inside the typerep, going further on.

#

type t

Mutable context used to memorize some info during the traversal of a typerep.
A new context is created before starting to enter the toplevel of a typerep.
Then it is passed to all `init`

calls that happen during the traversal of it.
The user of the generic functor is free to stuff there whatever context needs to be
available while creating a new value of type `'a Named.t`

end

#

type 'a t

end

#

Not all computations are arrow types. For example:

`'a computation = Type_struct.t`

`'a computation = Type_hash.t`

However, arrow types computation such as `of_sexp`

, `sexp_of`

, `json_of`

, etc. are
such a standard case that is seems reasonable to share this extra layer of functor for
it to build the `Named`

module.

#

module Ident : sig

#

type t

Runtime identifier for a generic computation. This is essentially a string whose purpose is to give reasonable error messages in case the dependency requirements for a generic are not met at runtime.

The field called `required`

is needed in order to build a generic computation module.
It is used to establish a set up that would explicitly list all the computation that
are required by an other computation to work.

Generic computations are a way to build dynamically some operations on types. It is possible to build computation on top of each other. This ident type will be the key to talk about other computations at the point of setting up the dependencies.

end

#

module type S = sig

#

type 'a t

#

exception Not_implemented of string * string

generic_ident * typename or info

#

val register0 : (module S0) -> unit

Extending an existing generic for a particular type name

The use of first class modules there is essentially because we cannot talk about a variable of kind * -> k val register1 : 'a 't Typerep.t -> ('a computation -> 'a 't computation) -> unit ...

#

val register : 'a Std_internal.Typerep.t -> 'a computation -> unit

special less scary type when the type has no parameters. this is equivalent as using register0

#

val of_typerep : 'a Std_internal.Typerep.t -> [

| `generic of 'a computation

]
main function : compute the generic computation from the typerep

#

exported to build a computation on top of a previous one

end

#

The `name`

is used for debug information only in case of Broken_dependency.
The `required`

is to handle dependencies between generics at runtime.
Example:
if `X`

is the module given to build a generic computation `G`

that depends on three
other computation `A,B,C`

then X.required shall be `A.ident ; B.ident ; C.ident`

end