4 ms·
module type Repo = sig type t type user val get_user : t -> name:string -> user option val get_all_users : t -> user list end mod
by ernst_klim 3y ago
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.
- _old_dude_ 3y agoYou still need virtual dispatch (or type pattern matching) if there is a variable which can contain either a BDRepo or a LoggedRepo(BDRepo).
- ernst_klim 3y agoNo, you don't. LocalRepo.t != Repo.t, and all the functions are purely static. LocalRepo.get_user won't work with other Repo.t, and other Repo.get_user won't work with LocalRepo.t. What you would need is to parametrize your further computation with a Repo module, as in module HttpHandler (R : Repo) : sig val handle_user : R.t -> HttpWriter.t -> username:string -> unit end = struct let handle_user repo writer ~username = match R.get_user repo username with | None -> Writer.write_status writer 404 | Some user -> Writer.write_body writer (User.to_string user) end and then to construct a handler module with a proper repo.