Hi,
can types also be created from PPXs?
I can only find examples where modules are extended with functions.
Related: could I also extended an existing type via a PPX?
Thx
Hi,
can types also be created from PPXs?
I can only find examples where modules are extended with functions.
Related: could I also extended an existing type via a PPX?
Thx
Hi,
Yes, you can generate type definitions. The exact way to do that will depend on what library you use to write the ppx, but for example when using ppx_deriving
, your plugin returns a structure
, which can contain value declarations (let ...
), but also type declarations (type ....
). (in .mli
files, it returns a signature
, which corresponds to anything that can go in a mli files). In other words, there are no surprises.
Related: could I also extended an existing type via a PPX?
Extending a type can mean different things. If you’re not just adding stuff to the module (but changing what was written in the file), you’ll need to go a bit lower level than ppx_deriving
and write an AST mapper. But yes, it’s possible to do things like automatically insert an extra constructor. You’re basically writing an ast → ast function.
It might be cause problems with other rewriter though, since these kind of transformations have different meaning depending on the order in which the transformations are applied, unlike more conventional things like [@@deriving]
.
can types also be created from PPXs?
Yes, types are just structure items. For example, here is a structure_item_mapper
that rewrites [%%example]
into type example=int
.
open Ast_mapper
open Ast_helper
open Asttypes
open Parsetree
open Longident
let structure_item_mapper mapper item =
begin match item with
| { pstr_desc =
Pstr_extension (({ txt = "example"; loc }, _), _)} ->
Str.type_ ~loc Nonrecursive [
Type.mk ~loc { txt = "example"; loc }
~manifest:(Typ.constr { txt = Lident "int"; loc } [])]
(* Delegate to the default mapper. *)
| x -> default_mapper.structure_item mapper x;
end
let example_mapper argv =
{
default_mapper with
structure_item = structure_item_mapper;
}
let () = register "example" example_mapper
Related: could I also extended an existing type via a PPX?
Yes, you may rewrite on type_declaration
. For example, here is a type_declaration_mapper
that catch [@@example]
attribute on variant declarations and add a constant constructor Example
.
let type_declaration_mapper mapper (declaration : type_declaration) =
begin
match
List.find_opt (fun ({ txt; loc = _ }, _) -> txt = "example")
declaration.ptype_attributes
with
| None -> default_mapper.type_declaration mapper declaration
| Some ({ txt = _; loc }, _) ->
let loc = declaration.ptype_name.loc in
match declaration.ptype_kind with
| Ptype_variant list ->
{ declaration with ptype_kind = Ptype_variant
(list @ [Type.constructor ~loc { txt = "Example"; loc }])}
| _ ->
raise (Location.Error
(Location.error ~loc "example can only extend variants"))
end
Note that if you want to write ppx that transforms types, you may want to define a ppx_deriving plugin.