Is it possible to get a type information for the deriver from the module signature?

Following up the question “Derived enums as bitmap masks” I have a lot of bitmask types and the need to decode them. So for example I have this code:

open Core
open Sexplib0.Sexp_conv

module Bitmask = struct
  let mask_list l f =
    l |> List.map ~f:(fun el -> (el, f el))

  let mask_decode x masks =
    List.fold_left ~f:(fun acc (f, mask) ->
       if x land mask <> 0 then f::acc else acc
    ) ~init:[] masks
end

type bm1 =
  | FEATURE1 [@value 0x20]
  | FEATURE2 [@value 0x80]
  [@@deriving enum, enumerate, sexp, show {with_path = false}]

type bm1_mask = bm1 list
  [@@deriving show, sexp]

let bm1_masks =
  Bitmask.mask_list all_of_bm1 bm1_to_enum

let decode_bm1 x =
  Bitmask.mask_decode x bm1_masks

type bm2 =
  | FEATURE1 [@value 0x20]
  | FEATURE2 [@value 0x80]
  [@@deriving enum, enumerate, sexp, show {with_path = false}]

type bm2_mask = bm2 list
  [@@deriving show, sexp]

let bm2_masks =
  Bitmask.mask_list all_of_bm2 bm2_to_enum

let decode_bm2 x =
  Bitmask.mask_decode x bm2_masks

And many, many pages of these bm* bitmask feature types to decode. So obvious thought is to functorize, or fold any other way this code. But the problem here is the derivers. Is it possible somehow to put this deriving code into the Functor/Monad/whatever fits? Or any other strategy to handle this?

I don’t think that derivers prevent functorization: the functor could just suppose that the type given in the module parameter comes with the functions produced by the derivers.


module type ENUM = sig
  type t
        [@@deriving enum, enumerate, sexp, show {with_path = false}]
end

module Make_bitmask (Enum : ENUM) = struct
  include Enum

  type mask = t list
        [@@deriving show, sexp]

  let masks = Bitmask.mask_list all Enum.to_enum

  let decode x =
    Bitmask.mask_decode x masks
end

Then you can define Bm1.t and Bm2.t just by instantiating this functor.

module Bm1 = Make_bitmask (struct
  type t =
    | FEATURE1 [@value 0x20]
    | FEATURE2 [@value 0x80]
        [@@deriving enum, enumerate, sexp, show {with_path = false}]
end)

module Bm2 = Make_bitmask (struct
  type t =
    | FEATURE1 [@value 0x20]
    | FEATURE2 [@value 0x80]
        [@@deriving enum, enumerate, sexp, show {with_path = false}]
end)
1 Like

Wow, truly OCaml has no limits! Thank you!

P.S. How to export the variant types here? Because with the provided code it is impossible to write something like Bm1.FEATURE1.

Oops! Here is a fix to my previous proposal. Unfortunately, it is a bit more verbose…

module type ENUM = sig
  type t
        [@@deriving enum, enumerate, sexp, show {with_path = false}]
end

module Make_bitmask (Enum : ENUM) = struct
  open Enum

  type mask = t list
        [@@deriving show, sexp]

  let masks = Bitmask.mask_list all Enum.to_enum

  let decode x =
    Bitmask.mask_decode x masks
end

module Bm1 = struct
  module Enum = struct
    type t =
      | FEATURE1 [@value 0x20]
      | FEATURE2 [@value 0x80]
          [@@deriving enum, enumerate, sexp, show {with_path = false}]
  end

  include Enum
  include Make_bitmask (Enum)
end

module Bm2 = struct
  module Enum = struct
    type t =
      | FEATURE1 [@value 0x20]
      | FEATURE2 [@value 0x80]
          [@@deriving enum, enumerate, sexp, show {with_path = false}]
  end

  include Enum
  include Make_bitmask (Enum)
end

let x = Bm1.FEATURE1
2 Likes