OO with rere? how to work-around?

Oh, I see.
Thanks a lot for this detail.

The problem is that this polymorphic data is only polymorphic at initialisation time.

Your sample does NOT contain any state, useful for my use case that I already explained in the first message.

I also have to repeat more than 3 times the use case that I already gave in the first message of this thread. And samples provided not always answer the initial question. And I don’t even complain about it, I just repeat politely again what I’m trying to do.

You can have any data as instance variable, not only your a, b and c fields.
For instance:

type instance = {print : unit -> unit}
let print_instance {print} = print ()

let make print_self self = {print = fun () -> print_self self}

let make_entity_a a = make (fun i -> Printf.printf " { %d }\n" i) a
let make_entity_b b = make (fun f -> Printf.printf " { %g }\n" f) b
let make_entity_c c = make (fun (c1, c2) -> Printf.printf "{ %d %d }\n" c1 c2) c
let () =
  let a = make_entity_a 3 in
  let b = make_entity_b 4.0 in
  let c = make_entity_c (10, 20) in
  let my_entities = [ a; b; c ] in
  List.iter print_instance my_entities
;;
 { 3 }
 { 4 }
{ 10 20 }

But here, you are not limited to this three kinds of entities.

And how do you update the state?

By adding a function update : unit -> instance as a field of the type instance.

type instance = {
  print : unit -> unit
  update : unit -> instance
}

That’s also why I prefer to use first-class modules and GADT instead of a recursive records of closures. This way I can have subtyping with my records-first class module (that you don’t have with simple record) and the implementation is more straightforward when you have a functional update of your data.

If I use first-class module, it’s to simplified the make constructor of the instance. With an update field, it will look like this:

let make print_self update_self self = ...

(* but this is similar to this *)
let make (print_self, update_self) self = ...

(* and this pair is like a record which is like a first-class module *)
let make meth self = ...

And I use a GADT because I don’t have to deal with a recursive type and so the implementation is simpler: I just have to dispatch each methods given by the module of methods to self.

What is not solved with the answer that was given to you?

It does. Your earlier update_instance function using records becomes:

type instance = { print : unit -> unit ;
                  update : unit -> instance }

let print_instance { print ; _ } = print ()
let update_instance { update ; _ } = update ()

let rec of_string s = { print = (fun () -> print_endline s) ;
                        update = (fun () -> let s = s ^ s in
                                            of_string s) }

let rec of_int i = { print = (fun () -> print_int i ; print_newline ()) ; 
                     update = (fun () -> let i = i + i in
                                         of_int i) }

let () =
  let entities = [ of_string "Hello" ; of_int 42 ] in
  let entities = List.map update_instance entities in
  let entities = List.map update_instance entities in
  List.iter print_instance entities
1 Like

And if you want a generic constructor for instance and build your previous a, b and c entities, you could do:

type instance = {
  print : unit -> unit;
  update : unit -> instance
}

let rec make print_self update_self self =
  let print () = print_self self in
  let update () = make print_self update_self (update_self self) in
  {print; update}

let make_a a = 
  let print a = Printf.printf " { % d }\n" a in
  let update a = succ a in
  make print update a

let make_b b =
  let print b = Printf.printf " { %g }\n" b in
  let update b = b +. 0.1 in
  make print update b

let make_c c =
  let print (c1, c2) = Printf.printf " { %d %d }\n" c1 c2 in
  let update (c1, c2) = (c1 + 10, c2 + 10) in
  make print update c

let () =
  [make_a 3; make_b 4.0; make_c (10, 20)]
  |> List.map update_instance
  |> List.iter print_instance
;;
 {  4 }
 { 4.1 }
 { 20 30 }
1 Like

I’ll try to give you the essence of OO emulation with different kinds of implementations, that will basically do the same simple and stupid thing. They all state the same things:

  • an entity is something that can be printed and updated (definition 1)
  • any thing that can be printed and updated is an entity (proposition 1)
  • any entity can be printed (proposition 2)
  • any entity can be updated (proposition 3)

I have some difficulty to imagine a simpler set of propositions. The first one is a definition of an entity (a type in OCaml parlance) and the three others are propositions that follow trivially from the definition.

Let’s now look at different ways to state that in OCaml, beginning with objects which is what you want to emulate. But before that, we will define your three previous entities, each of them in their own module.

module A = struct
  type t = int
  let print a = Printf.printf " { %d }\n" a
  let update a = a + 1
end

module B = struct
  type t = float
  let print a = Printf.printf " { %g }\n" a
  let update a = a +. 0.1
end

module C = struct
  type t = int * int
  let print (c1, c2) = Printf.printf " { %d %d }\n" c1 c2
  let update (c1, c2) = (c1 + 10, c2 + 10)
end

Reference implementation with objects

module Entity = struct
  (* here the definition 1 from above *)
  type t = < 
    print : unit -> unit;
    update : unit -> t;
  >

  (* now the proposition 1, which is how we construct an entity *)
  let make print_self update_self self : t = object
    val e = self
    method print () = print_self e
    method update () = {< e = update_self e >}
  end

  (* proposition 2 *)
  let print (e : t) = e#print ()

  (* proposition 3 *)
  let update (e : t) = e#update ()
end

And we can use them, as in the previous comments:

let () =
  let a = Entity_obj.make A.print A.update 3 in
  let b = Entity_obj.make B.print B.update 4.0 in
  let c = Entity_obj.make C.print C.update (10, 20) in
  [a; b; c;]
  |> List.map Entity_obj.update
  |> List.iter Entity_obj.print
;;
 { 4 }
 { 4.1 }
 { 20 30 }

Implementation with closures as previously

Now, we repeat the previous implementation with closures:

module Entity_closure = struct
  (* definition 1 *)
  type t = {
    print : unit -> unit;
    update : unit -> t;
  }

  (* proposition 1 *)
  let rec make print_self update_self self =
    let print () = print_self self in
    let update () = make print_self update_self (update_self self) in
    {print; update}

  (* proposition 2 *)
  let print e = e.print ()

  (* proposition 3 *)
  let update e = e.update ()
end

The definition is similar to the object case but with a record notation. Since the type is recursive we need to write a recursive constructor make, and here the instance variable self is hidden in each closures. You could note how the functional update is done with the recursive call to make and compare the way it’s done with object.

Implementation with GADT

Here, we keep the same interface as the previous ones, but the type t of entity is defined with a GADT.

module Entity_gadt = struct
  (* definition 1 *)
  type t = E : {
    print : 'a -> unit;
    update : 'a -> 'a;
    self : 'a;
  } -> t

  (* proposition 1 *)
  let make print update self = E {print; update; self;}

  (* proposition 2 *)
  let print (E e) = e.print e.self

  (* proposition 3 *)
  let update (E e) = E {e with self = e.update e.self}
end

Here, as with object, the instance variable self is hidden behind the existential GADT. We cannot access to self from the outside, but only via its methods. The definition of make is simpler than with object (you just have to pack the arguments together), and what is done with the definition of the methods in the object case is done, here, in the definition of the functions print and update (you could compare how similar they are).

Introduction of first-class module

I will let you do this by yourself (examples have already been given previously in the thread). First-class modules are used to only simplify calls to the make function. All the three previous implementations have the same interface:

module type Entity = sig
  (* definition 1 *)
  type t
  (* proposition 1 *)
  val make : ('a -> unit) -> ('a -> 'a) -> t
  (* proposition 2 *)
  val print : t -> unit
  (* proposition 3 *)
  val update : t -> t
end

and if you want to build an entity of kind A you have to pass its methods one by one:

make A.print A.update a

With first-class module you will have this common interface:

module type Entity = sig
  (* begin definition 1 *)
  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
  (* end definition 1 *)

  (* proposition 1 *)
  val make : 'a meth -> 'a -> t
  (* proposition 2 *)
  val print : t -> unit
  (* proposition 3 *)
  val update : t -> t
end

and now, to build an entity of kind A, you just have to pass its methods all at once:

make (module A) a

I hope this will make the problem (and its solution) clearer to you.

3 Likes

It looks like a use-case for a functor.
I.e. parametrize a module by another.
I only rarely have to do this. But I have worked on code using functors very heavily.
Sometimes, it is the only way to factorize your code.