As a layman I would describe them as implicits like in current Scala [0] but better (easier to reason about). One thing which they allow is ad-hoc polymorphism like typeclasses.
The ML programming language is known for its advanced module system. First, you have structured, which are your traditional idea of a module. Structures define types and data. Signatures describe the interface of a module and are like the module's "type." By making types abstract or not mentioning members, signatures achieve encapsulation of modules. Finally, you have functors, which are functions from modules to modules. The parameter is constrained by a signature, and the client can pass any module that conforms to the signature to get an output module that uses the passed module in its implementation.
The module system is used when a type must support certain operations. For example, the Set module (https://caml.inria.fr/pub/docs/manual-ocaml/libref/Set.html) defines a signature called OrderedType for types equipped with a comparison function, and any module that implements OrderedType can be used to make a set.
Notably, ML modules differ from Haskell typeclasses because more than one module can exist for the same combination of types. However, you must pass ML modules explicitly, whereas typeclass instances are passed implicitly. So, ML modules have more power than typeclasses, but at the cost of convenience.
Here is an example where modules are used to make a set:
module LtInt = struct
type t = int
(* Use built-in polymorphic comparison *)
let compare = compare
end
module GtInt = struct
type t = int
let compare lhs rhs = compare rhs lhs
end
module LtIntSet = Set.Make(LtInt)
module GtIntSet = Set.Make(GtInt)
However, modules can quickly get inconvenient:
module type Monoid = sig
type t
val op : t -> t -> t
val e : t
end
module Addition = struct
type t = int
let op = (+)
let e = 0
end
module Multiplication = struct
type t = int
let op = ( * )
let e = 1
end
module FoldLeft (M : Monoid) = struct
let fold_left = List.fold_left M.op M.e
end
Here, it's probably less verbose to just pass the operation and starting value directly to List.fold_left than to use modules! Debatably, this is as equally inconvenient as Haskell's newtype solution to multiple instances, but if there is only one instance, typeclasses are much cleaner to use.
Modular implicits are a long-awaited feature that will allow ML modules to be passed implicitly.
tutanchamun|6 years ago
https://arxiv.org/pdf/1512.01895.pdf
https://old.reddit.com/r/ocaml/comments/4qan0w/modular_type_...
[0] https://scala-lang.org
TheAsprngHacker|6 years ago
The module system is used when a type must support certain operations. For example, the Set module (https://caml.inria.fr/pub/docs/manual-ocaml/libref/Set.html) defines a signature called OrderedType for types equipped with a comparison function, and any module that implements OrderedType can be used to make a set.
Notably, ML modules differ from Haskell typeclasses because more than one module can exist for the same combination of types. However, you must pass ML modules explicitly, whereas typeclass instances are passed implicitly. So, ML modules have more power than typeclasses, but at the cost of convenience.
Here is an example where modules are used to make a set:
However, modules can quickly get inconvenient: Here, it's probably less verbose to just pass the operation and starting value directly to List.fold_left than to use modules! Debatably, this is as equally inconvenient as Haskell's newtype solution to multiple instances, but if there is only one instance, typeclasses are much cleaner to use.Modular implicits are a long-awaited feature that will allow ML modules to be passed implicitly.
https://www.cl.cam.ac.uk/~jdy22/papers/modular-implicits.pdf