package ppx_deriving

  1. Overview
  2. Docs
Type-driven code generation for OCaml

Install

Dune Dependency

Authors

Maintainers

Sources

ppx_deriving-6.0.2.tbz
sha256=7abacfc5934a1442047e110dc9128c0b0c6cd1fef19ee3429ac8b26ed3632b94
sha512=515e53d62aea4100bbbd8dc2421fc80ebe0976699dbd0e1786b672567fe9067ca8925731f2a72bca093bf759ee0d65a1a0caafeedbda28771bf0026425a8040b

Description

ppx_deriving provides common infrastructure for generating code based on type definitions, and a set of useful plugins for common tasks.

Tags

syntax

Published: 28 May 2024

README

[@@deriving]

deriving is a library simplifying type-driven code generation on OCaml.

deriving includes a set of useful plugins: show, eq, ord, enum, iter, map, fold, make, yojson, protobuf.

Sponsored by Evil Martians.

Note: since deriving was released by whitequark in 2014, the OCaml ppx ecosystem has changed a lot. For new projects wishing to create a new deriving plugin, we recommend using ppxlib directly. The module Ppxlib.Deriving provide functionality similar to deriving, better integrated with ppxlib, and offers a nicer API in some places. deriving is still maintained to keep existing plugins working as well as possible. Although note that the above deprecation note only covers the API and not the plugins (e.g. ppx_deriving.show, ppx_deriving.eq, ...).

Installation

deriving can be installed via OPAM:

opam install ppx_deriving

Buildsystem integration

To use deriving, only one modification is needed: you need to require via ocamlfind the package corresponding to the deriving plugin. This will both engage the syntax extension and link in the runtime components of the deriving plugin, if any.

For example, if you are using ocamlbuild, add the following to _tags to use the default deriving plugins:

<src/*>: package(ppx_deriving.std)

With Dune, you should add a preprocess directive to your target:

(executable
  (libraries whatever)
  (preprocess (pps ppx_deriving.show ppx_deriving.ord))
  (name blah))

Dune's pps directive allows faster preprocessing by linking the specified preprocessors into a single executable (documented here). This can significantly speed up compilation on large projects which use many derivers.

If you are using another buildsystem, just make sure it passes -package ppx_deriving.whatever to ocamlfind.

Usage

From a user's perspective, deriving is triggered by a [@@deriving plugin] annotation attached to a type declaration in structure or signature:

type point2d = float * float
[@@deriving show]

It's possible to invoke several plugins by separating their names with commas:

type point3d = float * float * float
[@@deriving show, eq]

It's possible to pass options to a plugin by appending a record to plugin's name:

type t = string
[@@deriving yojson { strict = true }]

It's possible to make deriving ignore a missing plugin rather than raising an error by passing an optional = true option, for example, to enable conditional compilation:

type addr = string * int
[@@deriving yojson { optional = true }]

It's also possible for many plugins to derive a function directly from a type, without declaring it first.

open OUnit2
let test_list_sort ctxt =
  let sort = List.sort [%derive.ord: int * int] in
  assert_equal ~printer:[%derive.show: (int * int) list]
               [(1,1);(2,0);(3,5)] (sort [(2,0);(3,5);(1,1)])

The [%derive.x:] syntax can be shortened to [%x:], given that the deriver x exists and the payload is a type. If these conditions are not satisfied, the extension node will be left uninterpreted to minimize potential conflicts with other rewriters.

Working with existing types

At first, it may look like deriving requires complete control of the type declaration. However, a lesser-known OCaml feature allows to derive functions for any existing type. Using Stdlib.fpclass as an example, show can be derived as follows:

# module M = struct
  type myfpclass = fpclass = FP_normal | FP_subnormal | FP_zero | FP_infinite | FP_nan
  [@@deriving show]
end;;
module M :
  sig
    type myfpclass =
      fpclass =
        FP_normal
      | FP_subnormal
      | FP_zero
      | FP_infinite
      | FP_nan
    val pp_myfpclass : Format.formatter -> fpclass -> unit
    val show_myfpclass : fpclass -> string
  end
# M.show_myfpclass FP_normal;;
- : string = "FP_normal"

The module is used to demonstrate that show_myfpclass really accepts Stdlib.fpclass, and not just M.myfpclass.

To avoid the need to repeat the type definition, it is possible to use ppx_import to automatically pull in the type definition. Attributes can be attached using its [@with] replacement feature.

Plugin conventions

It is expected that all deriving plugins will follow the same conventions, thus simplifying usage.

  • By default, the functions generated by a plugin for a type foo are called fn_foo or foo_fn. However, if the type is called type t, the function will be named foo. The defaults can be overridden by an affix = true|false plugin option.

  • There may be additional attributes attached to the AST. In case of a plugin named eq and attributes named compare and skip, the plugin must recognize all of compare, skip, eq.compare, eq.skip, deriving.eq.compare and deriving.eq.skip annotations. However, if it detects that at least one namespaced (e.g. eq.compare or deriving.eq.compare) attribute is present, it must not look at any attributes located within a different namespace. As a result, different ppx rewriters can avoid interference even if the attribute names they use overlap.

  • A typical plugin should handle tuples, records, normal and polymorphic variants; builtin types: int, int32, int64, nativeint, float, bool, char, string, bytes, ref, list, array, option, lazy_t and their Mod.t aliases; Result.result available since 4.03 or in the result opam package; abstract types; and _. For builtin types, it should have customizable, sensible default behavior. This default behavior should not be used if a type has a [@nobuiltin] attribute attached to it, and the type should be treated as abstract. For abstract types, it should expect to find the functions it would derive itself for that type.

  • If a type is parametric, the generated functions accept an argument for every type variable before all other arguments.

Plugin: show

show derives a function that inspects a value; that is, pretty-prints it with OCaml syntax. However, show offers more insight into the structure of values than the Obj-based pretty printers (e.g. Printexc), and more flexibility than the toplevel printer.

# type t = [ `A | `B of int ] [@@deriving show];;
type t = [ `A | `B of i ]
val pp : Format.formatter -> [< `A | `B of i ] -> unit = <fun>
val show : [< `A | `B of i ] -> string = <fun>
# show (`B 1);;
- : string = "`B (1)"

For an abstract type ty, show expects to find a pp_ty function in the corresponding module.

show allows to specify custom formatters for types to override default behavior. A formatter for type t has a type Format.formatter -> t -> unit:

# type file = {
  name : string;
  perm : int     [@printer fun fmt -> fprintf fmt "0o%03o"];
} [@@deriving show];;
# show_file { name = "dir"; perm = 0o755 };;
- : string = "{ name = \"dir\"; perm = 0o755 }"

It is also possible to use [@polyprinter]. The difference is that for a type int list, [@printer] should have a signature formatter -> int list -> unit, and for [@polyprinter] it's ('a -> formatter -> unit) -> formatter -> 'a list -> unit.

[@opaque] is a shorthand for [@printer fun fmt _ -> Format.pp_print_string fmt "<opaque>"].

The function fprintf is locally defined in the printer.

By default all constructors are printed with prefix which is dot-separated filename and module path. For example

# module X = struct type t = C [@@deriving show] end;;
...
# X.(show C);;
- : string = "X.C"

This code will create printers which return the string X.C, X is a module path and C is a constructor name. File's name is omitted in the toplevel. To skip all module paths the one needs to derive show with option with_path (which defaults to true)

# module X = struct type t = C [@@deriving show { with_path = false }] end;;
...
# X.(show C);;
- : string = "C"

Plugins: eq and ord

eq derives a function comparing values by semantic equality; structural or physical depending on context. ord derives a function defining a total order for values, returning a negative value if lower, 0 if equal or a positive value if greater. They're similar to Stdlib.(=) and Stdlib.compare, but are faster, allow to customize the comparison rules, and never raise at runtime. eq and ord are short-circuiting.

# type t = [ `A | `B of int ] [@@deriving eq, ord];;
type t = [ `A | `B of int ]
val equal : [> `A | `B of int ] -> [> `A | `B of int ] -> bool = <fun>
val compare : [ `A | `B of int ] -> [ `A | `B of int ] -> int = <fun>
# equal `A `A;;
- : bool = true
# equal `A (`B 1);;
- : bool = false
# compare `A `A;;
- : int = 0
# compare (`B 1) (`B 2);;
- : int = -1

For variants, ord uses the definition order. For builtin types, properly monomorphized (=) is used for eq, or corresponding Mod.compare function (e.g. String.compare for string) for ord. For an abstract type ty, eq and ord expect to find an equal_ty or compare_ty function in the corresponding module.

eq and ord allow to specify custom comparison functions for types to override default behavior. A comparator for type t has a type t -> t -> bool for eq or t -> t -> int for ord. If an ord comparator returns a value outside -1..1 range, the behavior is unspecified.

# type file = {
  name : string [@equal fun a b -> String.(lowercase a = lowercase b)];
  perm : int    [@compare fun a b -> compare b a]
} [@@deriving eq, ord];;
type file = { name : bytes; perm : int; }
val equal_file : file -> file -> bool = <fun>
val compare_file : file -> file -> int = <fun>
# equal_file { name = "foo"; perm = 0o644 } { name = "Foo"; perm = 0o644 };;
- : bool = true
# compare_file { name = "a"; perm = 0o755 } { name = "a"; perm = 0o644 };;
- : int = -1

Plugin: enum

enum is a plugin that treats variants with argument-less constructors as enumerations with an integer value assigned to every constructor. enum derives functions to convert the variants to and from integers, and minimal and maximal integer value.

# type insn = Const | Push | Pop | Add [@@deriving enum];;
type insn = Const | Push | Pop | Add
val insn_to_enum : insn -> int = <fun>
val insn_of_enum : int -> insn option = <fun>
val min_insn : int = 0
val max_insn : int = 3
# insn_to_enum Pop;;
- : int = 2
# insn_of_enum 3;;
- : insn option = Some Add

By default, the integer value associated is 0 for lexically first constructor, and increases by one for every next one. It is possible to set the value explicitly with [@value 42]; it will keep increasing from the specified value.

Plugins: iter, map and fold

iter, map and fold are three closely related plugins that generate code for traversing polymorphic data structures in lexical order and applying a user-specified action to all values corresponding to type variables.

# type 'a btree = Node of 'a btree * 'a * 'a btree | Leaf [@@deriving iter, map, fold];;
type 'a btree = Node of 'a btree * 'a * 'a btree | Leaf
val iter_btree : ('a -> unit) -> 'a btree -> unit = <fun>
val map_btree : ('a -> 'b) -> 'a btree -> 'b btree = <fun>
val fold_btree : ('a -> 'b -> 'a) -> 'a -> 'b btree -> 'a = <fun>
# let tree = (Node (Node (Leaf, 0, Leaf), 1, Node (Leaf, 2, Leaf)));;
val tree : int btree = Node (Node (Leaf, 0, Leaf), 1, Node (Leaf, 2, Leaf))
# iter_btree (Printf.printf "%d\n") tree;;
0
1
2
- : unit = ()
# map_btree ((+) 1) tree;;
- : int btree = Node (Node (Leaf, 1, Leaf), 2, Node (Leaf, 3, Leaf))
# fold_btree (+) 0 tree;;
- : int = 3

Plugin: make

make is a plugin that generates record constructors. Given a record, a function is generated that accepts all fields as labelled arguments and (); alternatively, if one field is specified as [@main], it is accepted last. The fields which have a default value (fields of types 'a option, 'a list, and fields with [@default] annotation) are mapped to optional arguments; the rest are mandatory. A field of form xs: ('a * 'a list) [@split] corresponds to two arguments: mandatory argument x and optional argument xs with types 'a and 'a list correspondingly.

type record = {
  opt  : int option;
  lst  : int list;
  def  : int [@default 42];
  args : (int * int list) [@split];
  norm : int;
} [@@deriving make];;
val make_record :
  ?opt:int ->
  ?lst:int list ->
  ?def:int ->
  arg:int ->
  ?args:int list ->
  norm:int ->
  unit ->
  record

To use make with a set of mutually recursive type definitions, simply attach a single [@@deriving make] attribute and it will derive a make_* function for each record type in the set.

The deriving runtime

deriving comes with a small runtime library, the Ppx_deriving_runtime module, whose purpose is to re-export the modules and types of the standard library that code producers rely on -- ensuring hygienic code generation.

By emitting code that references to Ppx_deriving_runtime.Array module instead of just Array, plugins ensure that they can be used in environments where the Array module is redefined with incompatible types.

Building ppx drivers

By default, deriving dynlinks every plugin, whether invoked as a part of a batch compilation or from the toplevel. If this is unsuitable for you for some reason, it is possible to precompile a ppx rewriter executable that includes several deriving plugins:

$ ocamlfind opt -predicates ppx_driver -package ppx_deriving_foo -package ppx_deriving_bar \
                -package ppx_deriving.main -linkpkg -linkall -o ppx_driver

Currently, the resulting ppx driver still depends on Dynlink as well as retains the ability to load more plugins.

Developing plugins

This section only explains the tooling and best practices. Anyone aiming to implement their own deriving plugin is encouraged to explore the existing ones, e.g. eq or show.

Tooling and environment

A deriving plugin is packaged as a Findlib library; this library should include a peculiar META file. As an example, let's take a look at a description of a yojson plugin:

version = "1.0"
description = "[@@deriving yojson]"
exists_if = "ppx_deriving_yojson.cma"
# The following part affects batch compilation and toplevel.
# The plugin package may require any runtime component it needs.
requires(-ppx_driver) = "ppx_deriving yojson"
ppxopt(-ppx_driver) = "ppx_deriving,./ppx_deriving_yojson.cma"
# The following part affects ppx driver compilation.
requires(ppx_driver) = "ppx_deriving.api"
archive(ppx_driver, byte) = "ppx_deriving_yojson.cma"
archive(ppx_driver, native) = "ppx_deriving_yojson.cmxa"

The module(s) provided by the package in the ppxopt variable must register the derivers using Ppx_deriving.register "foo" during loading. Any number of derivers may be registered; careful registration would allow a yojson deriver to support all three of [@@deriving yojson], [@@deriving of_yojson] and [@@deriving to_yojson], as well as [%derive.of_yojson:] and [%derive.to_yojson:].

It is possible to test the plugin without installing it by instructing deriving to load it directly; the compiler should be invoked as ocamlfind c -package ppx_deriving -ppxopt ppx_deriving,src/ppx_deriving_foo.cma .... The file extension is replaced with .cmxs automatically for native builds. This can be integrated with buildsystem, e.g. for ocamlbuild:

let () = dispatch (
  function
  | After_rules ->
    (* Assuming files tagged with deriving_foo are already tagged with
       package(ppx_deriving) or anything that uses it, e.g. package(ppx_deriving.std). *)
    flag ["ocaml"; "compile"; "deriving_foo"] &
      S[A"-ppxopt"; A"ppx_deriving,src/ppx_deriving_foo.cma"]
  | _ -> ()

Alternatively, you can quickly check the code generated by a ppx rewriter packaged with ocamlfind by running the toplevel as ocaml -dsource or utop -dsource, which will unparse the rewritten syntax tree into OCaml code and print it before executing.

Testing plugins

The main ppx_deriving binary can be used to output preprocessed source code in a human-readable form:

$ cat test.ml
type foo = A of int | B of float
[@@deriving show]
$ ocamlfind ppx_deriving/ppx_deriving \
    -deriving-plugin `ocamlfind query ppx_deriving`/ppx_deriving_show.cma \
    test.ml
type foo =
  | A of int
  | B of float [@@deriving show]
let rec (pp_foo : Format.formatter -> foo -> Ppx_deriving_runtime.unit) =
  ((let open! Ppx_deriving_runtime in
      fun fmt  ->
        function
        | A a0 ->
            (Format.fprintf fmt "(@[<2>T.A@ ";
             (Format.fprintf fmt "%d") a0;
             Format.fprintf fmt "@])")
        | B a0 ->
            (Format.fprintf fmt "(@[<2>T.B@ ";
             (Format.fprintf fmt "%F") a0;
             Format.fprintf fmt "@])"))
  [@ocaml.warning "-A"])

and show_foo : foo -> Ppx_deriving_runtime.string =
  fun x  -> Format.asprintf "%a" pp_foo x

Goals of the API

deriving is a thin wrapper over the ppx rewriter system. Indeed, it includes very little logic; the goal of the project is 1) to provide common reusable abstractions required by most, if not all, deriving plugins, and 2) encourage the deriving plugins to cooperate and to have as consistent user interface as possible.

As such, deriving:

  • Completely defines the syntax of [@@deriving] annotation and unifies the plugin discovery mechanism;

  • Provides an unified, strict option parsing API to plugins;

  • Provides helpers for parsing annotations to ensure that the plugins interoperate with each other and the rest of the ecosystem.

Using the API

Complete API documentation is available online.

Hygiene

A very important aspect of a syntax extension is hygiene. Consider a case where a deriving plugin makes assumptions about the interface provided by the List module: it will normally work as expected, but not in case where someone shadows the List identifier! This happens quite often in the OCaml ecosystem, e.g. the Jane Street [Core] library encourages developers to use open Core.Std.

Additionally, if your deriving plugin inserts user-provided expressions into the generated code, a name you are using internally may accidentally collide with a user-defined name.

With deriving, both of these problems are solved in three easy steps:

  • Create a quoter:

    let quoter = Ppx_deriving.create_quoter () in
    ...
    
  • Pass the user-provided expressions, if any, through the quoter, such as by using a helper function:

    let attr_custom_fn attrs =
      Ppx_deriving.(attrs |> attr ~deriver "custom_fn" |> Arg.(get_attr ~deriver expr)
                          |> quote ~quoter)
    
  • Wrap the generated code:

    let expr_of_typ typ =
      let quoter = ...
      and expr = ... in
      Ppx_deriving.sanitize ~quoter expr
    

    If the plugin does not accept user-provided expressions, sanitize expr could be used instead.

FAQ

The following is a list of tips for developers trying to use the ppx interface:

  • Module paths overwhelm you? Open all of the following modules, they don't conflict with each other: Longident, Location, Asttypes, Parsetree, Ast_helper, Ast_convenience.

  • Need to insert some ASTs? See ppx_metaquot; it is contained in the ppx_tools.metaquot package.

  • Need to display an error? Use Ppx_deriving.raise_errorf ~loc "Cannot derive Foo: (error description)" (doc); keep it clear which deriving plugin raised the error!

  • Need to derive a function name from a type name? Use Ppx_deriving.mangle_type_decl and Ppx_deriving.mangle_lid.

  • Need to fetch an attribute from a node? Use Ppx_deriving.attr ~prefix "foo" nod.nod_attributes (doc); this takes care of interoperability.

  • Put all functions derived from a set of type declarations into a single let rec block; this reflects the always-recursive nature of type definitions.

  • Need to handle polymorphism? Use Ppx_deriving.poly_fun_of_type_decl for derived functions, Ppx_deriving.poly_arrow_of_type_decl for signatures, and Ppx_deriving.poly_apply_of_type_decl for "forwarding" the arguments corresponding to type variables to another generated function.

  • Need to display a full path to a type, e.g. for an error message? Use Ppx_deriving.path_of_type_decl.

  • Need to apply a sequence or a binary operator to variant, tuple or record elements? Use Ppx_deriving.fold_exprs.

  • Don't forget to display an error message if your plugin doesn't parse any options.

License

deriving is distributed under the terms of MIT license.

Dependencies (6)

  1. ppxlib >= "0.32.0"
  2. ppx_derivers
  3. ocamlfind
  4. cppo >= "1.1.0" & build
  5. dune >= "1.6.3"
  6. ocaml >= "4.05.0" & < "5.3"

Dev Dependencies (1)

  1. ounit2 with-test

  1. alg_structs
  2. alt-ergo-lib >= "2.6.0"
  3. amf
  4. ansi-parse
  5. api-watch
  6. archetype >= "0.1.11"
  7. argon2 < "1.0.0"
  8. async-zmq >= "0.3.0"
  9. bap-frames < "2.1.1"
  10. biocaml >= "0.4.0" & < "0.7.0"
  11. biotk
  12. bistro = "0.2.0"
  13. bookaml >= "3.1"
  14. boomerang
  15. bpf
  16. builder-web >= "0.2.0"
  17. bulletml
  18. caisar
  19. caisar-ir
  20. caisar-xgboost
  21. caldav
  22. camlhighlight >= "4.0"
  23. caqti >= "1.2.0" & < "1.3.0"
  24. cbat-tools
  25. cbat-vsa
  26. cconv-ppx
  27. charrua-core >= "0.3" & < "0.6"
  28. checked_oint
  29. clangml >= "4.0.0beta1" & < "4.1.0"
  30. cohttp >= "0.20.1" & < "0.99.0"
  31. colibri2
  32. comby
  33. comby-kernel
  34. comby-semantic
  35. commons
  36. conduit = "0.15.4"
  37. conformist < "0.2.1"
  38. cookies
  39. coq-lsp >= "0.2.0+8.17"
  40. coq-serapi < "8.10.0+0.7.0" | >= "8.20.0+0.20.0"
  41. core-and-more
  42. cppffigen
  43. current < "0.2" | >= "0.4"
  44. current-albatross-deployer
  45. current_docker
  46. current_examples >= "0.4"
  47. current_git
  48. current_ocluster
  49. current_web >= "0.4"
  50. dap
  51. datakit-ci < "0.10.0"
  52. diff
  53. diskuvbox
  54. dkml-install
  55. dns = "0.19.1"
  56. dockerfile >= "1.3.0" & < "3.0.0"
  57. dune-expand
  58. earlybird >= "1.0.0"
  59. easy_logging_yojson >= "0.8.1"
  60. ego
  61. electrod < "0.1.6" | >= "0.5"
  62. eliom >= "6.0.0"
  63. elpi
  64. embedded_ocaml_templates = "0.5.1"
  65. equinoxe >= "0.2.0"
  66. errpy
  67. esgg
  68. farith
  69. flow_parser >= "0.80.0"
  70. flowtype >= "0.78.0"
  71. forester
  72. frama-c >= "25.0~beta"
  73. fred
  74. frenetic >= "5.0.0"
  75. fstar >= "0.9.6.0~alpha1"
  76. gdb
  77. git-split
  78. git_split
  79. gobba
  80. goblint
  81. gopcaml-mode >= "0.0.2"
  82. gopcaml-mode-merlin
  83. gospel >= "0.2.0"
  84. grpc
  85. guardian
  86. hack_parallel
  87. hockmd
  88. http-multipart-formdata >= "2.0.0"
  89. i3ipc >= "0.1.4"
  90. ibx >= "0.8.1"
  91. icalendar
  92. idd
  93. idds
  94. ipaddr >= "2.7.0" & < "2.8.0"
  95. irmin-bench >= "2.7.0"
  96. jhupllib
  97. jingoo >= "1.3.0"
  98. js_of_ocaml-webgpu
  99. js_of_ocaml-webidl
  100. jupyter >= "2.8.0"
  101. jwto
  102. karamel
  103. ketrew
  104. key-parsers >= "0.10.1"
  105. kinetic-client >= "0.0.9"
  106. kmt
  107. kremlin < "transition"
  108. lascar >= "0.7.0"
  109. lens >= "1.2.5"
  110. libbpf
  111. links >= "0.7.3"
  112. lua_parser
  113. MlFront_Cli
  114. m17n
  115. mazeppa
  116. minicaml >= "0.2.1"
  117. mirage-crypto-ec
  118. mirage-kv-mem < "3.2.1"
  119. mirage-nat < "3.0.0"
  120. mirage-net-xen >= "1.6.0" & < "1.7.1"
  121. mjson
  122. mmdb
  123. mock-ounit >= "0.1.1"
  124. modelica_ml < "0.2.0"
  125. morbig >= "0.10.3" & < "0.11.0"
  126. morsmall >= "0.2.0"
  127. msgpck < "1.1"
  128. multipart-form-data < "0.2.0"
  129. mutaml >= "0.3"
  130. nacc
  131. nbd >= "2.1.0" & < "4.0.3"
  132. nebula
  133. netkat
  134. netml
  135. nloge
  136. nocrypto >= "0.5.3"
  137. noise
  138. non_empty_list
  139. nuscr
  140. obeam < "0.1.0"
  141. obuilder
  142. obuilder-spec
  143. ocaml-basics != "0.4.0"
  144. ocaml-protoc-plugin
  145. ocaml-topexpect
  146. ocaml_db_model
  147. ocaml_pgsql_model
  148. ocamllint < "0.2.0"
  149. oci
  150. ocluster-api
  151. octez-l2-libs
  152. octez-libs
  153. octez-plompiler
  154. octez-smart-rollup-wasm-benchmark-lib
  155. odep
  156. ogen >= "0.1.3"
  157. opass >= "1.0.6"
  158. openapi
  159. openapi_router
  160. opine
  161. opium >= "0.15.0" & < "0.16.0"
  162. oraft
  163. orewa
  164. osnap
  165. otoggl
  166. otr >= "0.3.1" & < "0.3.5"
  167. override = "0.2.2"
  168. owork
  169. pa_ppx >= "0.15"
  170. packstream
  171. passmaker
  172. pattern < "0.2.0"
  173. pds >= "5.38"
  174. pds-reachability
  175. perf
  176. pg_query
  177. pgocaml >= "4.2"
  178. pgocaml_ppx >= "4.2" & < "4.3.0"
  179. phylogenetics
  180. pkcs11 >= "0.2.0" & < "0.10.0"
  181. pkcs11-driver
  182. planck >= "2.2.0"
  183. ppx_assert < "113.24.00"
  184. ppx_bench < "113.24.00"
  185. ppx_bin_prot < "113.24.00"
  186. ppx_compare < "113.24.00"
  187. ppx_conv_func < "113.24.00"
  188. ppx_core < "113.24.00"
  189. ppx_cstruct < "3.0.2"
  190. ppx_csv_conv < "113.24.00"
  191. ppx_custom_printf < "113.24.00"
  192. ppx_default
  193. ppx_deriving_cmdliner >= "0.6.0"
  194. ppx_deriving_hash
  195. ppx_deriving_madcast >= "0.2"
  196. ppx_deriving_popper
  197. ppx_deriving_protobuf >= "3.0.0"
  198. ppx_deriving_qcheck >= "0.2.0"
  199. ppx_deriving_yaml >= "0.2.0"
  200. ppx_deriving_yojson = "2.3" | >= "3.6.0"
  201. ppx_driver < "113.24.00"
  202. ppx_enum
  203. ppx_enumerate < "113.24.00"
  204. ppx_factory
  205. ppx_fail < "113.24.00"
  206. ppx_fields_conv < "113.24.00"
  207. ppx_gen_rec >= "2.0.0"
  208. ppx_here < "113.24.00"
  209. ppx_implicits >= "0.2.0"
  210. ppx_import
  211. ppx_inline_test < "113.24.00"
  212. ppx_jsobject_conv < "0.5.0"
  213. ppx_json_types
  214. ppx_meta_conv >= "2.0.1" & < "2.1.0" | >= "4.1.0"
  215. ppx_mica
  216. ppx_minidebug
  217. ppx_netblob >= "1.2.1"
  218. ppx_optcomp < "113.24.00"
  219. ppx_parser
  220. ppx_pbt >= "0.2.1"
  221. ppx_pipebang < "113.24.00"
  222. ppx_repr
  223. ppx_sexp_conv < "113.24.00"
  224. ppx_sexp_value < "113.24.00"
  225. ppx_ts
  226. ppx_type_conv >= "113.33.02+4.03" & < "v0.9.0"
  227. ppx_typerep_conv < "113.24.00"
  228. ppx_variants_conv < "113.24.00"
  229. ppx_xml_conv < "113.24.00"
  230. ppx_yojson >= "0.2.0" & < "1.1.0"
  231. prc
  232. prob-cache
  233. protocell
  234. protocol-9p >= "0.6.0" & < "0.12.0"
  235. protocol-9p-unix < "0.12.0"
  236. pyre-ast
  237. qcheck-lin = "0.1.1"
  238. qcheck-stm = "0.1.1"
  239. qcow < "0.10.0" | >= "0.11.0"
  240. qcow-format >= "0.3"
  241. qcstm >= "0.1.1"
  242. quests
  243. remu_ts
  244. reparse >= "3.0.0"
  245. res_tailwindcss
  246. rfsm >= "2.0"
  247. rpc >= "1.9.51" & < "5.9.0"
  248. safemoney
  249. sarif
  250. satyrographos
  251. sel
  252. serde_derive
  253. shared-block-ring >= "2.3.0" & < "3.0.0"
  254. slack
  255. snabela
  256. sparrow
  257. spectrum >= "0.4.0"
  258. sqlgg >= "0.4.4"
  259. starred_ml
  260. stitch
  261. styled-ppx
  262. swipl
  263. terminus
  264. tezos-scoru-wasm-helpers
  265. tezos-version >= "13.0"
  266. tezos-wasmer
  267. tezos-webassembly-interpreter >= "15.0"
  268. timmy
  269. timmy-lwt
  270. tls >= "0.7.1" & < "0.11.0"
  271. transmission-rpc
  272. travesty < "0.6.0"
  273. um-abt
  274. unmagic != "1.0.2"
  275. uri = "1.9.2"
  276. uring-trace
  277. usbmux >= "1.3.2"
  278. user-agent-parser
  279. uwt < "0.3.3"
  280. validate
  281. validator
  282. vchan >= "2.1.0" & < "3.0.0"
  283. visitors >= "20210127"
  284. vmnet = "1.1.0"
  285. vscoq-language-server
  286. wamp < "1.1"
  287. webidl
  288. websocket >= "2.0.0" & < "2.8"
  289. wikitext
  290. ws
  291. x509 >= "0.5.1" & < "0.6.0"
  292. yara
  293. yices2_bindings
  294. yuujinchou < "1.0.0"
  295. zarr
  296. zarr-eio
  297. zarr-lwt
  298. zarr-sync
  299. zeit
  300. zxcvbn >= "2.4+1"

Conflicts

None

OCaml

Innovation. Community. Security.