Hello,
I have read another thread about this. The following declaration is repeated in the
.mli and .ml. The line module RADIXOp : RADIXOperator is only in the .mli.
open Types
module type RADIXOperator = sig
type 'a radix_node = 'a
include module type of MakeRadixNode ( struct type 'a t = 'a radix_node end )
val new_node4 : unit → meta * node_type * bytes list * node array
val new_node16 : unit → meta * node_type * bytes list * node array
val add_child : bytes → meta * node_type * bytes list * node array →
node → meta * node_type * bytes list * node array
val insert_tree : tree → Bytes.t list → int64 → node
val search_after_terminating : node → Bytes.t list → int → int64 option
end
module RADIXOp : RADIXOperator
Should this be duplicated ? Is my code not adhering to the standards ?
I haven’t shown the full ADT as this compiles and works as expected.
module type RadixNode = sig
type 'a t
end
module MakeRadixNode ( RadixNode : RadixNode ) = struct
...
end
If you want to avoid repeating the module type declaration, you can put it in a separate module without interface, eg: radix_operator_intf.ml. This is a commony used pattern: eg all the *_intf.ml files in base/src at master · janestreet/base · GitHub.
To add a bit on top of Nicolas’ answer, I would also recommend this article about the “intf trick” that I found still relevant five years after its writing date.
(val EXPR : MTY) casts an expression EXPR as a module of type MTY, then include includes the module in the current module. This passes the build and fails at runtime, exactly what is desired during the initial rapid development phase.
It worth to note that it works because failwith (like exit) can return any type:
val failwith : string -> 'a
because it breaks the flow and the returned value has no sense. So the compiler accepts to cast (I’m not sure which term to use here) the result of failwith into a new module.
Alternatively, you can create an interface without a module if you’re just defining module types and types. Simply create an .mli file without an .ml and add (modules_without_implementation my_module) to your dune file.
I find it a bit cleaner than a standalone .ml since its clear it contains only interfaces and no code, but it has some drawbacks (can’t declare exceptions, can’t include it anywhere…).