OO with rere? how to work-around?

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

@vlaviron I also get errors with your sample.

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

Error: Syntax error:

$ ocaml c5.ml
File "./c5.ml", line 16, characters 26-35:
16 |      module Printable = M.printable
                               ^^^^^^^^^
Error: Syntax error

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)

I don’t understand this record implementation. It consists of data with no methods. This seems to be nothing to do with emulating OO.

Sorry, it’s a typo in my code. That should be M.Printable (with a capital P)

Didn’t put methods because here it’s really trivial to do so.

It’s not that trivial with GADT, first class modules and functors.

But there is no polymorphism.

@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

Well maybe. It looks pretty unappealing and difficult to extend. It is not OO as normally envisaged, but if it floats your boat go with it.

I share your opinion about it. The right way would be to use real objects but objects don’t work with rescript.

Your alternative also have a drawback: I’m not able to figure out the right syntax by myself.

The idea doesn’t come from me. It comes from other ocaml programmers who are explaining that we can use records to fake OO.

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.

I’m not sure what your goals are. But if you simply want to compile to JavaScript, I’m pretty sure js_of_ocaml handles them perfectly.

Thanks, Daniel, but as I already told you, the problem with joo is that I’ve never been able to use it by myself. Not even after several afternoons.

I had no difficulties with rescript. And maybe melange will fix OO.

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.

Could you provide a sample to show what you mean?

I will PR something to verse with the samples of this thread.

If you could expand the 3 with a functional update method it would be fine.

(because I will probably not be able to find it by myself)

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.

Isn’t it exactly the same than the solution I posted above?

What’s the difference?

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.