value level module packing and functors in OCaml - module

I wonder why one example fails and not the other.
(* this fails *)
(* (l fails to type check)
This expression has type 'a but an expression was expected of type
(module M.TFixU)
The module type M.TFixU would escape its scope
*)
let foldList1 (type ar) algr l =
let module M = FixT (ListIntF) in
let (module LU : M.TFixU) = l in
assert false
(* but this works *)
let foldList2 (type ar) algr l =
let (module LU : FixT(ListIntF).TFixU) = l in
assert false
complete code
module Higher = struct
type ('a, 't) app
module type NewType1 = sig
type 'a s
type t
val inj : 'a s -> ('a, t) app
val prj : ('a, t) app -> 'a s
end
module NewType1 (X : sig
type 'a t
end) =
struct
type 'a s = 'a X.t
type t
external inj : 'a s -> ('a, t) app = "%identity"
external prj : ('a, t) app -> 'a s = "%identity"
end
end
module Fix = struct
open Higher
module FixT (T : NewType1) = struct
module type T_Alg = sig
type a
val alg : (a, T.t) app -> a
end
module type TFixU = sig
module App : functor (A : T_Alg) -> sig
val res : A.a
end
end
type tFixU = (module TFixU)
end
end
module Pb = struct
open Higher
open Fix
(* intro *)
type 'r listIntF = Empty | Succ of (int * 'r)
module ListIntF = NewType1 (struct
type 'r t = 'r listIntF
end)
(* this fails *)
let foldList1 (type ar) algr l =
let module M = FixT (ListIntF) in
let (module LU : M.TFixU) = l in
(* (l fails to type check)
This expression has type 'a but an expression was expected of type
(module M.TFixU)
The module type M.TFixU would escape its scope
*)
let module T = LU.App (struct
type a = ar
let alg = algr
end) in
T.res
(* but this doesn't *)
let foldList2 (type ar) algr l =
let (module LU : FixT(ListIntF).TFixU) = l in
let module T = LU.App (struct
type a = ar
let alg = algr
end) in
T.res
end

In the first case, the type of l is unified with the type defined in the module M, which defines the module type. Since the type is introduced after the value l, which is a parameter in an eager language so it already exists, the value l receives a type that doesn't yet exist at the time of its creation. It is the soundness requirement of the OCaml type system that the value lifetime has to be enclosed with its type lifetime, or more simply each value must have a type. The simplest example is,
let x = ref None (* here `x` doesn't have a type since it is defined later *)
type foo = Foo;; (* the `foo` scope starts here *)
x := Some Foo (* foo escapes the scope as it is assigned to `x` via `foo option` *)
Another simplified example, that involves a function parameter is the following,
let foo x =
let open struct
type foo = Foo
end in
match x with
| Some Foo -> true (* again, type foo escapes the scope as it binds to `x` *)
| None -> false
A very good article that will help you understand in-depth scopes and generalization is Oleg Kiselyov's How OCaml type checker works -- or what polymorphism and garbage collection have in common.
Concerning the second case, you clearly specified the type of l using the applicative nature of OCaml functors. And since the typechecker knows that the lifetime of FixT(ListIntF).TFixU is greater than the lifetime of l it is happy.

Related

recursive module as a fix point in OCaml

Is it possible to write the module Nat as a fixpoint or a universal algebra for some functor NatF ?
module rec Nat : sig
type t = Z | S of Nat.t
end = struct
type t = Z | S of Nat.t
end
Presumably to_int would then be a regular fold
let rec to_int : Nat.t -> int = function Z -> 0 | S x -> 1 + to_int x
I am not sure that I fully understand your question, but it looks like that you want to define a fixed-point combinator on the type level, so that you can write,
module NatF = Fix(functor (F : F)(T:T) -> struct
type t = Z | S of F(T).t
end)
module R = NatF(struct type s type t = Z | S of s end)
It is possible, e.g.,
module type T = sig type t end
module type F = functor (T : T) -> T
module type F2F = functor (F : F) -> F
module type Fix = functor (F : F2F) -> F
module rec Fix : Fix = functor (F : F2F) -> F (Fix(F))
but the problem is that it will leave the resulting type R.t abstract, so you can't write the to_int function over R.t. Even if you will try to define the to_int function inside of the open-recursive functor, you won't be able to recurse over the F(T).t type of the S branch as it will be abstract.
So, I believe, that in the end, you will still have to rely either on the recursion that is implicit in the type definition or on recursive modules to tighten up the fixed point, e.g.,
module type T = sig type t end
module Nat (T : T) = struct
module type S = sig
type t = Z | S of T.t
end
end
module type F = functor (T : T) -> Nat(T).S
module type F2F = functor (F : F) -> F
module type Fix = functor (F : F2F) -> F
module rec Fix : Fix = functor (F : F2F) -> F (Fix(F))
module F = functor(F : F)(T : T) -> struct
type t = F(T).t = Z | S of T.t
end
module rec R : sig
type t = Z | S of R.t
end = Fix(F)(R)
rephrasing Ivg's answer and its issue (with a tweak to make it compile - interesting to understand "safe module")
module Ivg = struct
module type T = sig type t end
module type F = functor (T : T) -> T
module NatF (T : T) = struct
type t = Z | S of T.t
end
module type Fix = functor (F : F) -> T
module rec Fix : Fix =
functor (F : F) -> F (struct type t = Fix(F).t end) (*tweak - prevent loop at definition - cf "safe module" at https://caml.inria.fr/pub/papers/xleroy-recursive_modules-03.pdf *)
module Nat = Fix (NatF)
(* As mentioned by #ivg :
it will leave the resulting type Nat.t abstract,
so you can't write the to_int function over Nat.t.
Even if you will try to define the to_int function inside of the open-recursive functor,
you won't be able to recurse over the F(T).t type of the S branch as it will be abstract. *)
end
Whose solution can be approximated by
module type T = sig type t end
module type F = functor (T : T) -> T
module rec NatF : F =
functor (T : T) ->
struct
type t = Z | S of T.t
end
and FixedNat : T = struct
type t = NatF(FixedNat).t
end
Fix now statically know it is being applied to NatF.
But T is still abstract
I don't know any other way as of yet (modular implicit ?)
Another way of expressing Nat
(* type 't natF = Z | S of 't *)
module rec FixedNat : sig
module type Z = sig end
module type S = sig
val n : FixedNat.n
end
type n = Z | S of (module S)
end =
FixedNat
let rec to_int : FixedNat.n -> int = function
| Z -> 0
| S (module N) -> to_int N.n
which still does not express FixedNat as an explicit fix point of an algebra
Another approximate solution.
The encoding of modules fixed point can be done through its universal property for type level algebras
module Algebras = struct
(* type level natf_algebra : 'a natF -> 'a *)
module type natF_algebra = sig
type a
val zero : a
val succ : a -> a
end
(* A type level algebra lowered *)
type 'a natF_algebra = (module natF_algebra with type a = 'a)
(* is an ordinary value level algebra *)
type 'a natf = Z | S of 'a
type 'a natf_algebra = { alg : 'a natf -> 'a }
end
module Encoding = struct
open Algebras
(* Universal encoding *)
module type NatU = sig
module Ap : functor (F : natF_algebra) -> sig
val res : F.a
end
end
type natU = (module NatU)
(* recursive module *)
module rec FixedNat : sig
module type Z = sig end
module type S = sig
val n : FixedNat.n
end
type n = Z | S of (module S)
end =
FixedNat
end
module Equivalence = struct
open Algebras
open Encoding
let fixedNat_to_natU : FixedNat.n -> natU =
fun n ->
(module struct
module Ap (F : natF_algebra) = struct
let rec foldNat = function
| FixedNat.Z -> F.zero
| FixedNat.S (module N) -> foldNat N.n |> F.succ
let res = foldNat n
end
end)
let rec natU_to_fixedNat : natU -> FixedNat.n =
fun (module N) ->
let module M = N.Ap (struct
type a = FixedNat.n
let zero = FixedNat.Z
let succ x =
FixedNat.S
(module struct
let n = x
end)
end) in
M.res
end

How does one state that a module implements an interface so that a type in the signature is the same as another type?

I am creating monads in OCaml and need to compose them, so I created transformers. I implemented the regular monad in terms of the transformer with the Identity monad:
module type MONAD = sig
type 'a m
val (>>=) : 'a m -> ('a -> 'b m) -> 'b m
val return : 'a -> 'a m
end
module Identity : MONAD = struct
type 'a m = 'a
let (>>=) m f = f m
let return x = x
end
module OptionT (M : MONAD) : MONAD with type 'a m := ('a option) M.m = struct
type 'a m = ('a option) M.m
let (>>=) m f = M.(>>=) m (fun option ->
match option with
| Some x -> f x
| None -> M.return None)
let return x = M.return ## Some x
end
module Option = OptionT(Identity)
However, I can't do this:
open Option
let _ = (Some 1) >>= (fun x -> Some (x + 1))
The errors are:
(Some 1)
This expression has type 'a option
but an expression was expected of type 'b option Identity.m
Some (x + 1)
This expression has type 'a option
but an expression was expected of type 'b option Identity.m
If I try to fix the error with module Identity : MONAD with type 'a m = 'a I get an error at module Option = OptionT(Identity) that states that
The type `m' is required but not provided
It seems that now, 'a has replaced 'a m in the signature.
Doing
module Option : MONAD with type 'a m := 'a option = struct
type 'a m = 'a option
let (>>=) m f =
match m with
| Some x -> f x
| None -> None
let return x = Some x
end
works just fine.
How do I tell the compiler that a module implements a signature so that a type declared in the signature is the same as another type, while still keeping the signature's original type declaration?
It seems that now, 'a has replaced 'a m in the signature.
This the effect of destructive substitution, when you write
module Identity : MONAD with type 'a m := 'a
you are asking the compiler to substitute all instance of 'a m by 'a.
Contrarily, standard with constraint adds a type equality to the module type
module Identity : MONAD with type 'a m = 'a
Looking at your various examples, it seems that you have confused the two, and are using destructive substitution when you meant to add a type constraint:
module OptionT(X:Monad) : MONAD with type 'a m = 'a = …
(* or *) module Option : MONAD with type 'a m = 'a option = …
and not
module OptionT(X:Monad) : MONAD with type 'a m := 'a = …
(* nor *) module Option : MONAD with type 'a m := 'a option = …
This expression has type 'a option but an expression was expected of type 'b option Identity.m
And indeed, the compiler does not know anything about Identity except that its signature is MONAD. (: MONAD) is not something that merely helps the compiler, it hides all information about Identity except that its signature is MONAD.
So, you can add a type equality for that
module Identity : MONAD with type 'a m = 'a = ...
and it works.

OCaml use signature defined outside functor to limit visibility into produced module

I'm trying to write a functor that takes a pair of ordered things and produces another ordered thing (with ordering defined lexicographically).
However, I want the resulting "ordered type" to be abstract, rather than an OCaml tuple.
This is easy enough to do with an inline/anonymous signature.
(* orderedPairSetInlineSig.ml *)
module type ORDERED_TYPE = sig
type t
val compare : t -> t -> int
end
module MakeOrderedPairSet (X : ORDERED_TYPE) :
sig
type t
val get_fst : t -> X.t
val get_snd : t -> X.t
val make : X.t -> X.t -> t
val compare : t -> t -> int
end = struct
type t = X.t * X.t
let combine_comparisons fst snd =
if fst = 0 then snd else fst
let compare (x, y) (a, b) =
let cmp = X.compare x a in
let cmp' = X.compare y b in
combine_comparisons cmp cmp'
let get_fst ((x, y) : t) = x
let get_snd ((x, y) : t) = y
let make x y = (x, y)
end
I want to give my anonymous signature a name like ORDERED_PAIR_SET_TYPE and move it outside the definition of MakeOrderedPairSet, like so (warning: not syntactically valid) :
(* orderedPairSet.ml *)
module type ORDERED_TYPE = sig
type t
val compare : t -> t -> int
end
module type ORDERED_PAIR_SET_TYPE = sig
type t
type el
val get_fst : t -> el
val get_snd : t -> el
val make : el -> el -> t
val compare : t -> t -> int
end
module MakeOrderedPairSet (X : ORDERED_TYPE) :
(ORDERED_PAIR_SET_TYPE with type el = X.t) = struct
type t = X.t * X.t
let combine_comparisons fst snd =
if fst = 0 then snd else fst
let compare (x, y) (a, b) =
let cmp = X.compare x a in
let cmp' = X.compare y b in
combine_comparisons cmp cmp'
let get_fst ((x, y) : t) = x
let get_snd ((x, y) : t) = y
let make x y = (x, y)
end
with el being an abstract type in the signature that I'm trying to bind to X.t inside the body of MakeOrderedPairSet.
However, I can't figure out how to fit everything together.
(ORDERED_PAIR_SET_TYPE with type el = X.t) is the most obvious way I can think of to say "give me a signature that's just like this one, but with an abstract type replaced with a concrete one (or differently-abstract in this case)". However, it isn't syntactically valid in this case (because of the parentheses). Taking the parentheses off does not result in a valid "module-language-level expression" either; I left it on because I think it makes my intent more obvious.
So ... how do you use a named signature to restrict the visibility into a [module produced by a functor]/[parameterized module]?
If you don't want to add el to the exports of the module then there are two ways:
Use a substitution constraint:
ORDERED_PAIR_SET_TYPE with type el := X.t
That will remove the specification of el from the signature.
Use a parameterised signature. Unfortunately, that is not expressible directly in OCaml, but requires a bit of extra functor gymnastics around the definition of your signature:
module SET_TYPE (X : ORDERED_TYPE) =
struct
module type S =
sig
type t
val get_fst : t -> X.el
val get_snd : t -> X.el
val make : X.el -> X.el -> t
val compare : t -> t -> int
end
end
With that you can write:
module MakeOrderedPairSet (X : ORDERED_TYPE) : SET_TYPE(X).S = ...

Dealing with type name conflicts when include'ing two modules in OCaml

can anyone help me for the exercise 12.5 of Jason Hickey's book?
Basically, the question is how to avoid the following conflicting issue due to "include" in practice? Thanks.
# module type XSig = sig
type t
val x : t
end;;
# module A : XSig = struct
type t = int
let x = 0
end;;
# module B : XSig = struct
type t = int
let x = 1
end;;
# module C = struct
include A
include B
end;;
I don't know what the question precisely says, but regarding your code snippet, there are various very different way to interpret what you're trying to do.
What you are currently doing is sealing A and B under two abstract, incompatible signatures, then trying to mix them in a module, only to have a name conflict.
Maybe you just want to avoid the name conflict. The simplest solution, of course, is not to use the same names for both types A.t and B.t. Then you can "not include B":
module C = struct
include A
let x_b = B.x
end
The better solution is to use OCaml 3.12 destructive substitution for signatures, with type t := foo, to mask the type t from the module B:
module C = struct
include A
type u = B.t (* rename B.t into 'u' to avoid name conflict *)
include (B : XSig with type t := u) (* replaces 't' by 'u' *)
end
You may also want the types for modules A and B to be compatible. In this case you must not seal them with abstract types.
module type XSig = sig
type t
val x : t
end
module A = struct
type t = int
let x = 0
end
(* if you want to check that A can be sealed by XSig, do it here,
outside the main declaration *)
let _ = (module A : XSig)
module B = struct
type t = int
let x = 1
end
module C = struct
include (A : XSig with type t := int)
include (B : XSig with type t := int)
end
(* module C : sig
val x = int
end *)
In this example both types A.t and B.t are removed by the destructive subtitution :=. If you want your module C to have a type t, you can write either of:
module C = struct
type t = int
include (A : XSig with type t := t)
include (B : XSig with type t := t)
end
or, using non-destructive substitution (changes the type definition instead of removing it):
module C = struct
include (A : XSig with type t = int)
include (B : XSig with type t := t)
end
See the manual page for destructive substitution type type t := ... for more details, and the one on the classic with type t = ... construction for comparison.

Friend Modules in OCaml

I currently have two "layers" of modules that represent identifier-data relationships in a database.
The first layer defines identifier types, such as IdUser.t or IdPost.t while the second layer defines data types such as User.t or Post.t. I need all the modules of the first layer to be compiled before the modules of the second layer, because a Post.t must hold the IdUser.t of its author and the User.t holds the IdPost.t of the last five posts he visited.
Right now, IdUser.t provides functionality that should only ever be used by User.t, such as the ability to transform an IdUser.t into an IdUser.current: for security reasons, this transform must only ever be performed by the function User.check_password. Since IdUser and User are independent modules, I need to define those features as public functions and rely on conventions to avoid calling them anywhere outside of User, which is rather dirty. A symmetrical situation happens in IdPost.mine:
module IdUser : sig
type t
type current
val current_of_t : t -> current (* <--- Should not be public! *)
end = struct
type t = string
type current = string
let current_of_t x = x
end
module IdPost : sig
type t
type mine
val mine_of_t : t -> mine (* <--- Should not be public! *)
end = struct
type t = string
type mine = string
let mine_of_t x = x
end
module Post : sig
(* Should not "see" IdUser.current_of_t but needs IdPost.mine_of_t *)
val is_mine : IdUser.current -> IdPost.t -> IdPost.mine
end
module User : sig
(* Should not "see" IdPost.mine_of_t but needs IdUser.current_of_t *)
val check_password : IdUser.t -> password:string -> IdUser.current
end
Is there a way of defining an current_of_t : t -> current function in IdUser that can only be called from within module User ?
EDIT: this was a simplified example of one pair of modules, but there's an obvious solution for a single pair that cannot be generalized to multiple pairs and I need to solve this for multiple pairs — about 18 pairs, actually... So, I've extended it to be an example of two pairs.
So IdUser is in reality an existential type: For User there exists a type
IdUser.current such that the public IdUser.t can be lifted to it. There are a couple of ways to encode this: either functorize User as Gasche shows if statically managing the dependence is sufficient, or use first-class modules or objects if you need more dynamism.
I'll work out Gasche's example a bit more, using private type abbreviations for convenience and to show how to leverage translucency to avoid privatizing implementation types too much. First, and this might be a limitation, I want to declare an ADT of persistent IDs:
(* File id.ml *)
module type ID = sig
type t
type current = private t
end
module type PERSISTENT_ID = sig
include ID
val persist : t -> current
end
With this I can define the type of Posts using concrete types for the IDs but with ADTs to enforce the business rules relating to persistence:
(* File post.ml *)
module Post
(UID : ID with type t = string)
(PID : PERSISTENT_ID with type t = int)
: sig
val is_mine : UID.current -> PID.t -> PID.current
end = struct
let is_mine uid pid =
if (uid : UID.current :> UID.t) = "me" && pid = 0
then PID.persist pid
else failwith "is_mine"
end
The same thing with Users:
(* File user.ml *)
module User
(UID : PERSISTENT_ID with type t = string)
: sig
val check_password : UID.t -> password:string -> UID.current
end = struct
let check_password uid ~password =
if uid = "scott" && password = "tiger"
then UID.persist uid
else failwith "check_password"
end
Note that in both cases I make use of the concrete but private ID types. Tying all together is a simple matter of actually defining the ID ADTs with their persistence rules:
module IdUser = struct
type t = string
type current = string
let persist x = x
end
module IdPost = struct
type t = int
type current = int
let persist x = x
end
module MyUser = User (IdUser)
module MyPost = Post (IdUser) (IdPost)
At this point and to fully decouple the dependencies you will probably need signatures for USER and POST that can be exported from this module, but it's a simple matter of adding them in.
One way that seems to work at least on your simplified example is to group IdUser and User inside a same module:
module UserAndFriends : sig ... end = struct
module IdUser : sig
...
end = struct
...
end
module User = struct
...
end
end
module Post : sig
val create : (* <--- Should not "see" IdUser.current_of_t *)
author:IdUser.current -> title:string -> body:string -> IdPost.t
end
Hiding the dangerous function(s) in the signature of UserAndFriends gives the result you desire. If you do not want to make a big file containing both IdUser and User, you can use option -pack of ocamlc to create UserAndFriends. Note that in this case, you must craft your Makefile carefully so that the .cmi files of IdUser and User are not visible when compiling Post. I am not the Makefile specialist for Frama-C, but I think we use separate directories and position the compiler option -I carefully.
I suggest you parametrize Post (and possibly User for consistency) by a signature for the IdUser module : you would use a signature with current_of_t for User, and one without for Post.
This guarantee that Post doesn't use IdUser private features, but the public interface of IdUser is still too permissive. But with this setup, you have reversed the dependencies, and IdUser (the sensitive part) can control its use directly, give itself (with the private part) to IdUser and restrict the public signature to the public parts.
module type PrivateIdUser = sig
val secret : unit
end
module type PublicIdUser = sig
end
module type UserSig = sig
(* ... *)
end
module MakeUser (IdUser : PrivateIdUser) : UserSig = struct
(* ... *)
end
module IdUser : sig
include PublicIdUser
module User : UserSig
end
= struct
module IdUser = struct
let secret = ()
end
module User = MakeUser(IdUser)
include IdUser
end
module Post = struct
(* ... *)
end
Edit : Pascal Cuoq's concurrent -- in the temporal sense -- solution is alos very nice. Actually it's simpler and has less boilerplate. My solution adds an abstraction that allows for slightly more modularity, as you can define User independently of IdUser.
I think which solution is best probably depends on the specific application. If you have a lot of different modules that use PrivateIdUser private information, then using functors to write them separately instead of bundling everyone in the same module can be a good idea. If only User needs to be in the "private zone" and it's not very big, then Pascal's solution is a better choice.
Finally, while being forced to explicit Private and Public interfaces can be seen as an additional burden, it is also a way to make the access properties of different modules more explicit that using the position inside the module hierarchy.
It's possible to achieve fine-grained control over signatures with a combination of recursive modules, first-class modules and GADTs, but the limitation would be that all modules should then be inside the same top-level module and unpackings of first-class modules inside the recursive modules should be done in each function separately (not on the module-level as it would cause runtime exception Undefined_recursive_module):
module rec M1 : sig
module type M2's_sig = sig
val a : int
val c : float
end
module type M3's_sig = sig
val b : string
val c : float
end
type _ accessor =
| I'm_M2 : M2.wit -> (module M2's_sig) accessor
| I'm_M3 : M3.wit -> (module M3's_sig) accessor
val access : 'a accessor -> 'a
type wit
val do_it : unit -> unit
end = struct
module type M2's_sig = sig
val a : int
val c : float
end
module type M3's_sig = sig
val b : string
val c : float
end
type _ accessor =
| I'm_M2 : M2.wit -> (module M2's_sig) accessor
| I'm_M3 : M3.wit -> (module M3's_sig) accessor
module M1 = struct
let a = 1
let b = "1"
let c = 1.
end
let access : type a. a accessor -> a =
function
| I'm_M2 _ -> (module M1)
| I'm_M3 _ -> (module M1)
type wit = W
let do_it () =
let (module M2) = M2.(access ## I'm_M1 W) in
let (module M3) = M3.(access ## I'm_M1 W) in
Printf.printf "M1: M2: %d %s M3: %d %s\n" M2.a M2.b M3.a M3.b
end
and M2 : sig
module type M1's_sig = sig
val a : int
val b : string
end
module type M3's_sig = sig
val b : string
val c : float
end
type _ accessor =
| I'm_M1 : M1.wit -> (module M1's_sig) accessor
| I'm_M3 : M3.wit -> (module M3's_sig) accessor
val access : 'a accessor -> 'a
type wit
val do_it : unit -> unit
end = struct
module type M1's_sig = sig
val a : int
val b : string
end
module type M3's_sig = sig
val b : string
val c : float
end
type _ accessor =
| I'm_M1 : M1.wit -> (module M1's_sig) accessor
| I'm_M3 : M3.wit -> (module M3's_sig) accessor
module M2 = struct
let a = 2
let b = "2"
let c = 2.
end
let access : type a. a accessor -> a =
function
| I'm_M1 _ -> (module M2)
| I'm_M3 _ -> (module M2)
type wit = W
let do_it () =
let (module M1) = M1.(access ## I'm_M2 W) in
let (module M3) = M3.(access ## I'm_M2 W) in
Printf.printf "M2: M1: %d %f M3: %d %f\n" M1.a M1.c M3.a M3.c
end
and M3 : sig
module type M1's_sig = sig
val a : int
val b : string
end
module type M2's_sig = sig
val a : int
val c : float
end
type _ accessor =
| I'm_M1 : M1.wit -> (module M1's_sig) accessor
| I'm_M2 : M2.wit -> (module M2's_sig) accessor
val access : 'a accessor -> 'a
type wit
val do_it : unit -> unit
end = struct
module type M1's_sig = sig
val a : int
val b : string
end
module type M2's_sig = sig
val a : int
val c : float
end
type _ accessor =
| I'm_M1 : M1.wit -> (module M1's_sig) accessor
| I'm_M2 : M2.wit -> (module M2's_sig) accessor
module M3 = struct
let a = 3
let b = "3"
let c = 3.
end
let access : type a. a accessor -> a =
function
| I'm_M1 _ -> (module M3)
| I'm_M2 _ -> (module M3)
type wit = W
let do_it () =
let (module M1) = M1.(access ## I'm_M3 W) in
let (module M2) = M2.(access ## I'm_M3 W) in
Printf.printf "M3: M1: %s %f M2: %s %f\n" M1.b M1.c M2.b M2.c
end
let () =
M1.do_it ();
M2.do_it ();
M3.do_it ()