module type Repo = sig
type t
type user
val get_user : t -> name:string -> user option
val get_all_users : t -> user list
end
module LoggedRepo (R : Repo) : sig
(* Resulting module is of type Repo plus create function *)
include Repo
val create : delegate:R.t -> logger:Logger.t -> t
end = struct
type t = { delegate : R.t; logger : Logger.t }
type user = R.user
let create ~delegate ~logger = { delegate; logger }
let get_user repo name =
Logger.debug repo.logger "Get user by name";
R.get_user repo.delegate name
let get_all_users repo =
Logger.debug repo.logger "Get all users";
R.get_all_users repo.delegate
end
In languages like C it would be harder since they don't have notion of interface and implementation, so you would have to implement that as an object-like structure of function pointers and opaque structs for state.
The point is, for delegate you don't need lately bound methods and virtual dispatching, i.e. oop, only the notion of interface and implementation supported (or emulated), and then you can create implementations that wrap another implementations.