Typegist
Type introspection.
Warning. Typegist is unstable, see design notes and todos.
The typegist
library:
Stdlib.Type
module available in OCaml 5.1 with an additional Type.Gist
submodule for reflecting OCaml types as values.Stdlib.Fun
module with a Fun.Generic
module which has a few sample generic functions.See description examples and a generic function template.
Open the module to use it, this only redefines the Type
and Fun
modules in your scope.
module Type : sig ... end
Type introspection.
module Fun : sig ... end
Function manipulation and generic functions.
Similar to Balestrieri et al.'s low level view (§2.3.4). However, the representation is not extensible, a few case are dropped, some representations are unified, some specializations are added.
Typegist.Fun.Generic.Meta
for the functions in Typegist.Fun.Generic
). We use the type level defunctionalization of Yallop et al. to allow metadata values to depend on the type of represented values. One open question is where exactly to place the metadata, see the TODO.Type.Gist.sum
, has one case for arbitrary variants and other cases for common Stdlib variant types like lists. If there's no interest in the specialisation a generic view can easily be obtained with Type.Gist.Sum.to_variant
. Similar schemes are provided by Type.Gist.maplike
, Type.Gist.arraylike
. Scalar values are all grouped under Type.Gist.scalar
for which a few generic operations are provided in Type.Gist.Scalar
.Meta
placement. Must be nailed down before any release. See this section.For now we just have one dimensional bigarrays as arrays. Should we have the other dimensions, or just Genarray ? Where should we put it from a generic point of view ? Type.Gist.arraylike
is decidely 1D. A few alternatives:
Array1
and one case for Genarray
. and Type.Gist.Arraylike.to_array_module
reshapes Genarray
to Array1
Genarray
case and Type.Gist.Arraylike.to_array_module
reshapes Genarray
to Array1
Array1
(what we have now) except we add an int array
that has the dimensions of a Genarray from which it was reshapedMap
? I doubt (no way to capture the module argument Ord
as a parameter when writing the type Map.Make(Ord).t
). The current scheme requires a functor application to embed a map. It's quite ugly.None
we can't with Type.Gist.(case "None" @@ ctor None))
. Should open the representation of Type.Gist.Field.t
?Meta
key or keep them ? Do a review once a codec with default values on absence has been written.Type.Gist.t.Rec
and Type.Gist.rec'
rename to Unlazy
and unlazy ?Type.Gist.arraylike.String
and Type.Gist.arraylike.Bytes
it's unclear whether we want the char gist.Typegist.Fun.Generic.{equal,compare}
clarify and document the behaviour on Typegist.Type.Gist.maplike
, Typegist.Type.Gist.arraylike
and variant cases. The implementation on Hashtbl.t
are inefficient, the stdlib doesn't help much.Stdlib.Seq
to Typegist.Type.Gist.sum
? Feels a bit odd to mix in corecursion in thereFun.Generic.compare
and Fun.Generic.random
are quite inefficient.Difficult to find the right design. Basically we can have it everywhere like now, or only in the toplevel cases or in a single case at the toplevel. This may lead to different attachement interpretations though.
Tried to simply shift them in every toplevel case and let, for example, the gist of a field define the meta of the field but it is problematic with a field whose gist is a recursion step (at a meta to the rec step ?). Also our intepretation in the pretty printer example is different. The meta for the field redefines the whole field printing. While the pretty printer of the field's gist redefines the value. Is it important for other processors ?
In general it looks easier if we don't have to redefine the meta of existing gists to be able to specify metadata at a given point (e.g. the metadata of fields). The drawback is that processors need to document a bit more precisely which meta is relevant for them and we get more lookups (the field's meta and its gist).
Also tried to have meta in the parent case for cases which are variants (Type.Gist.Scalar
, Type.Gist.Scalar
. etc.) but in the end I shifted them back into the subvariants themselves despite the types looking heavier (Type.Gist.scalar
, Type.Gist.sum
, etc.). It makes the types meta-aware without needing to carry something on the side. The drawback is that generic meta lookup on a gist is likely slower.
The current scheme ends up making a lots of lookups. For this having only meta as a toplevel case is likely better. It also makes the AST more lightweight. Given the current generic function examples, except maybe for the pretty printer, that would be entirely sufficient. But once we get to for example, type-directed uis. things may look a bit different, especially at the field level.