{"id":18013024,"url":"https://github.com/sim642/ppx_easy_deriving","last_synced_at":"2026-02-04T15:35:07.829Z","repository":{"id":233239294,"uuid":"487222815","full_name":"sim642/ppx_easy_deriving","owner":"sim642","description":"Easily define PPX derivers without boilerplate and runtime overhead","archived":false,"fork":false,"pushed_at":"2024-06-24T09:19:37.000Z","size":163,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-31T22:12:15.956Z","etag":null,"topics":["ocaml","ppx","ppx-deriver","ppx-extension","ppxlib"],"latest_commit_sha":null,"homepage":"","language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sim642.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-30T08:25:55.000Z","updated_at":"2024-08-22T23:29:49.000Z","dependencies_parsed_at":"2024-10-30T03:32:06.081Z","dependency_job_id":null,"html_url":"https://github.com/sim642/ppx_easy_deriving","commit_stats":null,"previous_names":["sim642/ppx_easy_deriving"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sim642/ppx_easy_deriving","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sim642%2Fppx_easy_deriving","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sim642%2Fppx_easy_deriving/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sim642%2Fppx_easy_deriving/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sim642%2Fppx_easy_deriving/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sim642","download_url":"https://codeload.github.com/sim642/ppx_easy_deriving/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sim642%2Fppx_easy_deriving/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260072819,"owners_count":22954926,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ocaml","ppx","ppx-deriver","ppx-extension","ppxlib"],"created_at":"2024-10-30T03:19:40.696Z","updated_at":"2026-02-04T15:35:02.810Z","avatar_url":"https://github.com/sim642.png","language":"OCaml","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ppx_easy_deriving\n\n[![ci workflow status](https://github.com/sim642/ppx_easy_deriving/actions/workflows/ci.yml/badge.svg)](https://github.com/sim642/ppx_easy_deriving/actions/workflows/ci.yml)\n[![GitHub release status](https://img.shields.io/github/v/release/sim642/ppx_easy_deriving)](https://github.com/sim642/ppx_easy_deriving/releases)\n[![opam package status](https://badgen.net/opam/v/ppx_easy_deriving)](https://opam.ocaml.org/packages/ppx_easy_deriving)\n\nEasily define PPX derivers without boilerplate and runtime overhead.\n\n\n## Installation\n```console\nopam install ppx_easy_deriving\n```\n\n## Examples\n\n### Equal\nThe following shows multiple ways of defining a PPX deriver for a standard `equal` function with various trade-offs.\n\n#### Simple\nThe simplest way is to define `equal` (here called `easy_equal2`) for binary product and sum types, i.e. pairs and eithers.\nUsing an isomorphism, all algebraic datatypes can be handled using the simple binary constructs and their base cases (unit and empty type).\nThis is very similar to the simple derivers of [ppx_type_directed_value](https://github.com/janestreet/ppx_type_directed_value), but with lower runtime overhead.\n\n\u003c!-- $MDX file=example/ppx_easy_equal/ppx_easy_equal.ml,part=easy_equal2 --\u003e\n```ocaml\nmodule EasyEqual2Arg: Simple.Variant.S =\nstruct\n  (** Name of deriver. *)\n  let name = \"easy_equal2\"\n\n  (** Type of derived [easy_equal2] function for type [t]. *)\n  let typ ~loc t = [%type: [%t t] -\u003e [%t t] -\u003e bool]\n\n  (** [easy_equal2] function for [unit] type. *)\n  let unit ~loc = [%expr fun () () -\u003e true]\n\n  (** [easy_equal2] function for a pair of types with given [easy_equal2] functions [e1] and [e2]. *)\n  let both ~loc e1 e2 = [%expr fun (l1, l2) (r1, r2) -\u003e [%e e1] l1 r1 \u0026\u0026 [%e e2] l2 r2]\n\n  (** [easy_equal2] function for the empty type. *)\n  let empty ~loc = [%expr fun _ _ -\u003e true]\n\n  (** [easy_equal2] function for an either of types with given [easy_equal2] functions [e1] and [e2]. *)\n  let either ~loc e1 e2 =\n    [%expr fun l r -\u003e\n      match l, r with\n      | Either.Left l, Either.Left r -\u003e [%e e1] l r\n      | Either.Right l, Either.Right r -\u003e [%e e2] l r\n      | _, _ -\u003e false\n    ]\n\n  (** Apply isomorphism to given [easy_equal2] function.\n      This allows {!both} and {!either} to be applied to tuples, records and (polymorphic) variants of arbitrary size. *)\n  let apply_iso ~loc easy_equal2 ~f ~f':_ =\n    [%expr fun l r -\u003e [%e easy_equal2] ([%e f] l) ([%e f] r)]\nend\n\n(* Create full deriver from simple deriver and register it with ppxlib. *)\nmodule EasyEqual2Deriver = Deriver.Make (Simple.Variant.Reduce (EasyEqual2Arg))\nlet _ = EasyEqual2Deriver.register ()\n```\n\n#### Product\nThe isomorphisms of simple deriver definitions still incur some runtime overhead.\nA more efficient way to define `equal` (here called `easy_equal`) is to define it for entire products and sums in one go, instead of decomposing them to binary reductions.\nThese definitions are more involved, especially `variant` since it also handles inline records in constructor arguments.\nHowever, the deriver defined this way is as efficient as a dedicated `equal` deriver, e.g. from [ppx_deriving.eq](https://github.com/ocaml-ppx/ppx_deriving#plugins-eq-and-ord) or [ppx_compare](https://github.com/janestreet/ppx_compare).\n\n\u003c!-- $MDX file=example/ppx_easy_equal/ppx_easy_equal.ml,part=easy_equal --\u003e\n```ocaml\nmodule EasyEqualArg: Product.Variant.S =\nstruct\n  (** Name of deriver. *)\n  let name = \"easy_equal\"\n\n  (** Type of derived [easy_equal] function for type [t]. *)\n  let typ ~loc t = [%type: [%t t] -\u003e [%t t] -\u003e bool]\n\n  (** [easy_equal] function body for a product type with list of given [easy_equal] functions for the elements. *)\n  let product_body ~loc es pel per =\n    let body =\n      let esl = Pat_exp.to_exps ~loc pel in\n      let esr = Pat_exp.to_exps ~loc per in\n      Util.map3 (fun e l r -\u003e\n          (* Apply [easy_equal] function per element. *)\n          [%expr [%e e] [%e l] [%e r]]\n        ) es esl esr\n    in\n    (* Reduce per-element [easy_equal] results to a result for the entire product. *)\n    Util.reduce ~unit:[%expr true] ~both:(fun acc x -\u003e\n        [%expr [%e acc] \u0026\u0026 [%e x]]\n      ) body\n\n  (** [easy_equal] function for a product type with list of given [easy_equal] functions for the elements.\n      [pe_create] allows creating patterns and expressions of the actual product type. *)\n  let product ~loc ~pe_create es =\n    let pel = pe_create ~prefix:\"l\" in\n    let per = pe_create ~prefix:\"r\" in\n    let body = product_body ~loc es pel per in\n    let pl = Pat_exp.to_pat ~loc pel in\n    let pr = Pat_exp.to_pat ~loc per in\n    [%expr fun [%p pl] [%p pr] -\u003e [%e body]]\n\n  (** [easy_equal] function for a (polymorphic) variant type with list of given [easy_equal] functions for the (polymorphic) variant constructors. *)\n  let variant ~loc ces =\n    let cases = List.map (fun (c, c2, _es, es2) -\u003e\n        let pel = c ~prefix:\"l\" in\n        let per = c ~prefix:\"r\" in\n        let pel2 = c2 ~prefix:\"l\" in\n        let per2 = c2 ~prefix:\"r\" in\n        let body = product_body ~loc es2 pel2 per2 in\n        let pa = Pat_exp.to_pat ~loc pel in\n        let pb = Pat_exp.to_pat ~loc per in\n        (* Equality case for one constructor with given product argument. *)\n        case ~lhs:[%pat? [%p pa], [%p pb]]\n          ~guard:None\n          ~rhs:body\n      ) ces\n    in\n    let fallback =\n      (* Inequality case as fallback. *)\n      case ~lhs:[%pat? _, _]\n        ~guard:None\n        ~rhs:[%expr false]\n    in\n    let body = pexp_match ~loc [%expr l, r] (cases @ [fallback]) in\n    [%expr fun l r -\u003e [%e body]]\nend\n\n(* Create full deriver from product deriver and register it with ppxlib. *)\nmodule EasyEqualDeriver = Deriver.Make (Product.Variant.Make (EasyEqualArg))\nlet _ = EasyEqualDeriver.register ()\n```\n\n#### Performance\nThese two easy derivers are benchmarked against [ppx_deriving.eq](https://github.com/ocaml-ppx/ppx_deriving#plugins-eq-and-ord), [ppx_compare](https://github.com/janestreet/ppx_compare), [ppx_type_directed_value](https://github.com/janestreet/ppx_type_directed_value) and [refl](https://github.com/thierry-martinez/refl).\nThe benchmark involves an equality check on a 60-field record where only the last fields differ.\n\nThe table shows the product version (ppx_easy_deriving) being on par with ppx_deriving and ppx_compare.\nThe simple version (ppx_easy_deriving2) is notably slower but still faster than ppx_type_directed_value (ppx_type_directed_equal) and refl.\n\n|                         |       Rate |  refl | ppx_type_directed_equal | ppx_easy_deriving2 | ppx_deriving | ppx_compare | ppx_easy_deriving |\n| -----------------------:| ----------:| -----:| -----------------------:| ------------------:| ------------:| -----------:| -----------------:|\n|                    refl |   605433/s |    -- |                    -73% |               -91% |         -97% |        -97% |              -97% |\n| ppx_type_directed_equal |  2245269/s |  271% |                      -- |               -65% |         -90% |        -90% |              -90% |\n|      ppx_easy_deriving2 |  6412029/s |  959% |                    186% |                 -- |         -71% |        -72% |              -72% |\n|            ppx_deriving | 22347280/s | 3591% |                    895% |               249% |           -- |         -1% |               -1% |\n|             ppx_compare | 22595512/s | 3632% |                    906% |               252% |           1% |          -- |               -0% |\n|       ppx_easy_deriving | 22639836/s | 3639% |                    908% |               253% |           1% |          0% |                -- |\n\nSee the full benchmark in [`example/ppx_easy_equal_bench/`](./example/ppx_easy_equal_bench/).\n\n### Lattice\n[`example/ppx_lattice/`](./example/ppx_lattice/) contains examples of additional product deriver helper functors by defining a deriver for product lattices with the following signature:\n\u003c!-- $MDX file=example/ppx_lattice_test/ppx_lattice_test.ml,part=lattice --\u003e\n```ocaml\nmodule type Lattice =\nsig\n  type t\n\n  (* reduce2-like function *)\n  val leq: t -\u003e t -\u003e bool (* reduce by conjunction *)\n\n  (* map2-like function *)\n  val join: t -\u003e t -\u003e t\n\n  (* create-like function *)\n  val bot: unit -\u003e t\n\n  (* reduce-like function *)\n  val is_bot: t -\u003e bool (* reduce by conjunction *)\n\n  (* map-like function *)\n  val relift: t -\u003e t (* not really a lattice operation *)\nend\n```\n\n## Other libraries\n\n* [ppx_type_directed_value](https://github.com/janestreet/ppx_type_directed_value) — has runtime overhead on every call of derived function.\n* [ppx_derive_at_runtime](https://github.com/janestreet/ppx_derive_at_runtime) — also has runtime overhead (TODO: include in benchmarks).\n* [refl](https://github.com/thierry-martinez/refl) — requires runtime representations of types.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsim642%2Fppx_easy_deriving","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsim642%2Fppx_easy_deriving","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsim642%2Fppx_easy_deriving/lists"}