You can have a look on the paper ML module mania which present a type safe, separetly compiled and extensible interpreter for the Lua language. In the same spirit, you have the work of Oleg Kiselyov on tagless-final embedded DSL. Or from the same author than the paper on ligthweight higher-kinded types, you have their proposal for modular implicit which go further.
About the last paper, imagine that you want to write a function show
that works on mutliple data types but not uniformly. In a lot of languages, this is done with dynamic dispatching. Since OCaml doesn’t have runtime type representation, we have to rely on something which is closed to a static dispatching, and we can do this with first-class modules.
module type Showable = sig
type t
val show : t -> string
end
let show (type a) (module M : Showable with type t = a) x = M.show x
Now you can define a module for each type that you want to convert to a string
, and you have to explicitly pass it to your fonction show
.
module Int_show = struct
type t = int
let show = string_of_int
end
module Float_show = struct
type t = float
let show = string_of_float
end
# show (module Int_show) 3;;
- : string = "3"
# show (module Float_show) 3.4;;
- : string = "3.4"
The idea behind modular implicit is that in this situation, the module to use to show your value is automatically infered by the compiler in such a way that you don’t have to explicitly pass it to your function. This will be very useful, especially when the module in question is the result of a functor application. Indeed, imagine that you know how to show values of two types a
and b
, then you know how to show a pair of type a * b
. To encode this you have to write a functor:
module Pair_show (A : Showable) (B : Showable):
Showable with type t = A.t * B.t = struct
type t = A.t * B.t
let show (x,y) = Printf.sprintf "(%s, %s)" (A.show x) (B.show y)
end
# show (module Pair_show (Int_show) (Float_show)) (1, 2.5);;
- : string = "(1, 2.5)"