ppx_open

Idiomatic selective `open`s in OCaml
README

A ppx rewriter that provides idiomatic selective opens 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

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).

Install
Published
27 Aug 2021
Maintainers
Sources
ppx_open-0.1.tbz
sha256=9ab1fa1a7102184a23bbca5138f2294b4daa60ca8014d37256c76bc31788ce43
sha512=92a719a9ef129373c3098776789b8812743af80a5dbf57f7e54b7087690428f179d242f2fa73eafa241d22e85b0e0516dd4756ab1cc29c207e592626f4c8586b
Dependencies
odoc
with-doc
menhir
>= "20210419"
base
>= "v0.14.1"
ppxlib
>= "0.22.2"
dune
>= "2.8"
ocaml
>= "4.12.0" & < "4.13.0"
Reverse Dependencies