In both cases you have to wrap the result of update:
(* Previous example *)
let update_instance (Instance ((module M), t)) = Instance ((module M), M.update t)
(* New version *)
let update_instance (module M : INSTANCE) =
(module struct
module Printable = M.printable
let self = M.Printable.update M.self
end : INSTANCE)
But it does suggest that you may want to use a mutable field to store the current value instead.
Something like that might work:
(* Previous version *)
type instance =
Instance : { p : (module PRINTABLE with type t = 'a); mutable t : 'a } -> instance
let update_instance (Instance i) = let (module M) = i.p in i.t <- M.update i.t
(* New version *)
module type INSTANCE = sig
module Printable : PRINTABLE
val self : Printable.t ref
end
let update_instance (module M : INSTANCE) =
M.self := M.Printable.update !M.self
module type PRINTABLE = sig
type t
type u
val print : t -> unit
val make: u -> t
val update: t -> t
end
type instance =
Instance : (module PRINTABLE with type t = 'a) * 'a -> instance
let print_instance (Instance ((module M), t)) = M.print t
let update_instance (module M : INSTANCE) =
(module struct
module Printable = M.printable
let self = M.Printable.update M.self
end : INSTANCE)
module PrintString = struct
type t = string
type u = string
let print s = print_endline s
let make s = s
let update s = s ^ s
end
module PrintInt = struct
type t = int
type u = int
let print i = print_int i ; print_newline ()
let make i = i
let update i = i + i
end
let of_string u =
Instance ((module PrintString), PrintString.make u)
let of_int u =
Instance ((module PrintInt), PrintInt.make u)
let () =
let entities = [ of_string "Hello" ; of_int 42 ] in
let entities = List.map update_instance entities in
List.iter print_instance entities
Your implementation of update_instance is incorrect. That method is creating a new instance, not mutating an existing one. For the GADT implementation it should be:
let update_instance (Instance ((module M) as m, t)) =
Instance (m, M.update t)
@polymorphism: Yes, it’s polymorphic as I already explained, because here I aggregate all needed fields for all different kind of entities.
Here how it looks like with a print and an update methods:
type entity = {
a: int;
b: float;
c: int * int;
update: entity -> entity;
print: entity -> unit;
}
let a = 0
let b = 0.0
let c = (0, 0)
let update e = e
let print _ = ()
let dummy = { a; b; c; update; print; }
let make_entity_a () =
{ dummy with a = 3;
update = (fun e -> { e with a = succ e.a } );
print = (fun e -> Printf.printf " { %d }\n" e.a);
}
let make_entity_b () =
{ dummy with b = 4.0;
update = (fun e -> { e with b = e.b +. 0.1 } );
print = (fun e -> Printf.printf " { %g }\n" e.b);
}
let make_entity_c () =
{ dummy with c = (10, 20);
update = (fun e -> let c1, c2 = e.c in { e with c = (c1 + 10, c2 + 10) } );
print = (fun e -> let c1, c2 = e.c in
Printf.printf " { %d %d }\n" c1 c2);
}
let () =
let a = make_entity_a () in
let b = make_entity_b () in
let c = make_entity_c () in
let my_entities = [ a; b; c ] in
let my_entities =
List.map (fun e -> e.update e) my_entities
in
List.iter (fun e -> e.print e) my_entities
It’s not so difficult. Here with the last example of Printable:
module Printable = struct
module type S = sig
type t
val print : t -> unit
val update : t -> t
end
type 'a meth = (module S with type t = 'a)
type t = P : {meth : 'a meth; self : 'a} -> t
let make meth self = P {meth; self}
let print (P v) =
let (module M) = v.meth in
M.print v.self
let update (P v) =
let (module M) = v.meth in
P {v with self = M.update v.self}
end
module PrintInt = struct
type t = int
let print i = print_int i ; print_newline ()
let update i = i + i
end
module PrintString = struct
type t = string
let print s = print_endline s
let update s = s ^ s
end
let () =
let entities = Printable.([
make (module PrintString) "hello";
make (module PrintInt) 42;
]) in
entities
|> List.map Printable.update
|> List.iter Printable.print
;;
hellohello
84
No, it’s also fairly easy with first class modules. If you don’t like the GADT you could use a recursive record of closures as with your solution (but I find the implementation less straightforward).
But these are (presumably) for records holding closures. In other words, using my first example but with record syntax instead of first-class module syntax (the code emitted by both should be the same). The syntax for beginners using closures should be natural for them and is how these thing were done in functional languages before first-class modules and GADTs came along, which I agree look a bit challenging at first sight.
I would also go along with the idea that there needs to be more beginner documentation on this, if only by extracting the three example implementations (closure, wrapper module, GADT) that I offered.
Maybe if you explain which problems you ran into we can help you.
I don’t think there’s anything particularly complicated in using js_of_ocaml. Maybe checkout brr’s webpage howto which has both bare-bone and dune instructions to get you started.
I did.
I’m not angry no one gave me a solution when I did.
I know answers are made by volunteers, and that they do it for free, and that they can not do it all the time.
The equivalent to the first example I gave but using a record instead of a first-class module is:
type instance = { print : unit -> unit }
let print_instance { print } = print ()
let of_string s = { print = fun () -> print_endline s }
let of_int i = { print = fun () -> print_int i ; print_newline () }
let () =
List.iter print_instance [ of_string "Hello" ; of_int 42 ]
The reason why I used a first-class module was because you had expressed uncertainty about how they worked. The implementations are in effect the same.
The version of your code that I responded to did not use closures to hold data. I am beginning to lose track of where you are and there is a limit to what can usefully be said, so I am probably going to end it here.