This commit is contained in:
swrup 2025-11-11 02:07:51 +01:00
parent aa2ff7b2f0
commit 2f3113f55d
11742 changed files with 1223940 additions and 0 deletions

View file

@ -0,0 +1,189 @@
ppx_compare
===========
Generation of fast comparison and equality functions from type
expressions and definitions.
Ppx_compare is a ppx rewriter that derives comparison and equality
functions from type representations. The scaffolded functions are
usually much faster than ocaml's `Pervasives.compare` and
`Pervasives.(=)`. Scaffolding functions also gives you more
flexibility by allowing you to override them for a specific type and
more safety by making sure that you only compare comparable values.
Syntax
------
Type definitions: `[@@deriving compare, equal]`
Expressions: `[%compare: TYPE]`, `[%equal: TYPE]` and `[%compare.equal: TYPE]`
Types, record fields: `[@compare.ignore]`, `[@equal.ignore]`
Basic usage
-----------
We use `ppx_deriving`/`ppx_type_conv`, so type definitions are annotated this way:
```ocaml
type s = v * w [@@deriving compare]
```
This will generate `compare_s : s -> s -> int` function that relies on
`compare_v : v -> v -> int` and `compare_w : w -> w -> int`.
Compare is not DWIM (do what I mean): it will scaffold a fast well behaved comparison
(reflexive, transitive, symmetric...) function however it does not try to follow any
"natural ordering". For instance arrays of characters are not sorted lexicographically.
Base types (options,int,array,lists,char,floats...) have the same comparison order as
Pervasives.compare (provided their type parameters also do for the polymorphic ones).
Comparisons for these types must be brought in scope with `open Base`, `open Core`, or
`open Ppx_compare_lib.Builtin`.
Records fields are compared in the order they are defined (left to right); tuples fields
are compared left to right. When we compare two branches of a sum whichever ones comes
first in the definition is considered lowest. Variants compare in the order they are
listed (increasing top-to-bottom). Polymorphic variants use the same ordering as the
ocaml runtime.
The same applies to equality functions.
### Float equality
The functions derived by `[@@deriving equal]` are consistent with the
compare functions derived by `[@@deriving compare]` and in particular
do not respect IEEE float comparison.
Calling `compare` for type `t`s
-------------------------------
In compliance (or conformance) with Janestreet's coding standard we assume that
type named `t` are the main types in a module and
```ocaml
type t = S.t * T.t [@@deriving compare]
```
will call the functions `S.compare` and `T.compare` instead of calling `S.compare_t` and
`T.compare_t`. This will also generate a `compare : t -> t -> int` function.
The same applies to equality functions.
Signature
---------
`type t [@@deriving compare]` in a module signature will add `val compare : t -> t -> int`
in the signature.
The same applies to equality functions.
Comparison without a type definition
------------------------------------
Sometimes you just want a comparison without having to create a new type. You can create
such a comparison function using the `[%compare: ..]` extension point:
```ocaml
let gt x y = [%compare: float * int * [`A | `B | `C] ] x y
```
You can use the type `_`, in which case the corresponding values will be
ignored (i.e. compared using `fun _ _ -> 0`). For instance:
```ocaml
assert ([%compare: _ list] [ true ] [ false ] = 0);
assert ([%compare: _ list] [] [ false ] <> 0);
```
The same applies to equality functions.
You can also check for equality using `[%compare.equal: ..]`, which
produces a function that returns `true` precisely when `[%compare:
..]` returns `0`. `[%equal: ..]` is preferred over `[%compare.equal:
..]` and in particular is expected to be slightly faster. However,
`[%compare.equal: ..]` can come in handy for types that only have
`[@@deriving compare]`. In particular, support for `[@@deriving
equal]` was added long after the project started, which means that
many types out there only support `[@deriving compare]`.
Ignoring part of types
----------------------
The comparison ignores any part of the type declaration that is under
a `[@compare.ignore]` annotation:
```ocaml
type t = (float [@compare.ignore]) * string
[@@deriving compare]
```
The same applies for `[@@deriving equal]` by using
`[@equal.ignore]`. In order to ignore part of a type for both
comparison and equality, you can simply use `[@ignore]`. However, be
aware that the general `[@ignore]` attribute will apply to any deriver
that recognize it, not just `compare` and `equal`.
Note that if you use both the `compare` and `equal` derivers, you need
to use either both `[@compare.ignore]` and `[@equal.ignore]` or
`[@ignore]`. However, you cannot use only one of them.
For convenience, you can also ignore record fields instead of the
type of record field. In other words,
```ocaml
type t =
{ a : (float [@compare.ignore])
; b : string
}
[@@deriving compare]
```
can be abbreviated:
```ocaml
type t =
{ a : float [@compare.ignore]
; b : string
}
[@@deriving compare]
```
Local-accepting compare functions
----------------------------
This ppx includes the option to support local allocation, a nonstandard OCaml extension
available at: https://github.com/ocaml-flambda/ocaml-jst
In both structures and signatures, `[@@deriving compare ~localize]` (and similarly for
`equal`) generates definitions with the following types, in addition to the usual
definitions:
```
(* Monomorphic types *)
val compare__local : local_ t -> local_ t -> int
val equal__local : local_ t -> local_ t -> bool
(* Parameterized types *)
val compare__local
: (local_ 'a -> local_ 'a -> int)
-> local_ 'a t
-> local_ 'a t
-> int
val equal__local
: (local_ 'a -> local_ 'a -> bool)
-> local_ 'a t
-> local_ 'a t
-> bool
```
You can also use the `[%compare_local: _]`, `[%equal_local: _]` and
`[%compare_local.equal: _]` extension points to generate the corresponding
types and functions.
For types named something other than `t`, the naming pattern is similar
to the non-local versions:
```ocaml
type foo
val compare_foo__local : local_ foo -> local_ foo -> int
```