Swapping between different implementations of a signature

I have a library called ‘consensus’ in my project with a signature that is exported defined in the file ‘consensus.mli’. There are two files implementing this signature in ‘consensus_impl.ml’ and ‘consensus_chained_impl.ml’.

I then hardcode a selection of implementation in the file ‘consensus.ml’:

include Types
include Util
include Consensus_chained_impl (* hardcode to select this implementation *)

How can I allow users of the library to select an implementation in code rather than hardcoding the choice inside the library?

If you use dune to build your library, you might be interested in reading about virtual libraries and variants , which seem to be what you want.

You could provide a syntactic signature Consensus.S and let users input it in their functors, which will allow swapping the implementation after the fact. E.g. user code:

module Using_consensus(C : Consensus.S) = struct
  (* Now they can use the consensus signature with C *)
end

module Using_consensus_impl = Using_consensus(Consensus_impl)
(* Or if they want, *)
module Using_consensus_chained = Using_consensus(Consensus_chained)

This is one of the motivating use-cases for first-class modules. Simply do

include (val
 (if ... then
    (module Consensus_impl : S)
  else
    (module Consensus_chained_impl : S)))

where S is the signature of the modules.

See more examples over at the manual:

https://v2.ocaml.org/manual/firstclassmodules.html#p:fst-mod-example

Cheers,
Nicolas

1 Like