package ppx_open
Install
Dune Dependency
Authors
Maintainers
Sources
sha256=9ab1fa1a7102184a23bbca5138f2294b4daa60ca8014d37256c76bc31788ce43
sha512=92a719a9ef129373c3098776789b8812743af80a5dbf57f7e54b7087690428f179d242f2fa73eafa241d22e85b0e0516dd4756ab1cc29c207e592626f4c8586b
Description
A ppx rewriter that provides idiomatic selective open
s in OCaml.
Published: 27 Aug 2021
README
ppx-open
A ppx rewriter that provides idiomatic selective open
s in OCaml.
Syntax
ppx_open
adds selective open
constructs, wrapped inside [%%open: {|...|}]
, that may expose
modules
module types
values
types (and their constructors)
of a given module.
For instance, the following code:
(* Foo.ml *)
let val_1 = 1
type type_1 = A | B | C
type 'a type_2 = D of 'a
module Mod_1 = struct
let val_2 = 3
end
(* Bar.ml *)
[%%open: {|
Foo.( val_1 as renamed
, type type_1
, type type_2 (..) as renamed_type_2
, module Mod_1) |}]
is rewritten into:
open (struct
let renamed = Foo.val_1
type type1 = Foo.type_1
type 'a renamed_type_2 = 'a Foo.type_2 = D of 'a
module Mod_1 = Foo.Mod_1
end)
Optionally, one may prefer to use the syntax: {%%open| ... |}
which is syntactic equivalent to [%%open: {| ... |}]
.
Modules and Module Types
The grammar for modules and module types is defined by
module_item ::= module module_ident [as module_ident]
module_type_item ::= module type module_ident [as module_ident]
module_ident ::= <upper identifier>
The above grammar is designed to mimic the syntax of OCaml (and hence should be relatively intuitive). Modules and module types are specified by an "upper identifer", an identifier beginning with a uppercase letter e.g. Foo
, Bar
, S_with_syntax
, etc.
ppx-open
implements the opening of modules and module types via module (and module type) aliases. The optional as module_ident
allows for local aliases. For example:
(* monad.ml *)
module type S = sig
type 'a t
val bind : 'a t -> f:('a -> 'b t) -> 'b t
val return : 'a -> 'a t
end
module Option : S with type 'a t = 'a option = struct
type 'a t = 'a option = Some of 'a | None
let return x = Some x
let bind m ~f =
match m with
| Some x -> f x
| None -> None
end
(* Bar.ml *)
[%%open: {| Monad.(module Option as Option_monad, module type S as Monad) |}]
is rewritten to:
open (struct
module Option_monad = Monad.Option
module type Monad = Monad.S
end)
Values
The opening of values is similar to Modules and Module Types. They have the following grammar:
value_item ::= value_ident [as value_ident]
value_ident ::= <lower identifier>
Let value bindings are used to alias values, for example:
(* Foo.ml *)
let print ?(suffix=" world") prefix = print_endline (prefix ^ suffix)
(* Bar.ml *)
[%%open: {| Foo.(print as foo_print) |}]
is rewritten to:
open (struct
let foo_print = Foo.print
end)
Types
🚧 Warning: Relies on compiler internals 🚧
There are 2 kinds of type items:
closed:
type t
open:
type t (..)
An opened type item results in opening a type and exposing the type definition locally. Whereas a closed type item results a type alias.
This results in the following grammar for type items:
type_item ::= type type_ident type_kind [as type_ident]
type_kind ::= (..) | epsilon
For example:
(* Foo.ml *)
type 'a type_1 = A | B | C | D of int | E of 'a
type ('a, 'b) type_2 = Pair of ('a * 'b)
(* Bar.ml *)
[%%open: {| Foo.(type type_1, type type_2 (..) as t) |}]
is rewritten to:
open (struct
(* A type alias *)
type 'a type_1 = 'a Foo.type_1
(* Exposes type definition (constructors in this case) *)
type ('a, 'b) t = ('a, 'b) Foo.type_2 = Pair of ('a * 'b)
end)
Both expansions rely on typing information from the environment (obtained using internal compiler libraries) to generate the correct typing manifest (and obtain the type declaration for the type definition).