News for hackers. Stuff that matters.
Re:OCaml: Objects by kxyvr on Monday March 16, 2015 @02:20PM (#9213231)
In fact you can, but it becomes a little verbose. Thankfully, much of the verbosity can be eliminated by macros. In any case, here's an example with lenses that shows how to do this:

  (* Create a basic lens type *)
  type ('a, 'b) t = {
    get : 'a -> 'b;
    set : 'b -> 'a -> 'a
  }
  
  (* A base lens, kind of like a base class *)
  type 'a myrecord_lens = {
      foo : ('a,int) t;
      label : ('a,string) t
  }
  
  (* Record 1 *)
  type myrecord1 = {
      foo : int;
      bar : float;
      label : string
  }
  let myrecord1 = {
      foo = 1;
      bar = 2.3;
      label = "myrecord1"
  }
  let foo_lens = { 
      get = (fun x -> x.foo); 
      set = (fun v x -> { x with foo = v })
  }
  let bar_lens = { 
      get = (fun x -> x.bar); 
      set = (fun v x -> { x with bar = v })
  }
  let label_lens = { 
      get = (fun x -> x.label); 
      set = (fun v x -> { x with label = v })
  }
  let myrecord1_lens = {
      foo = foo_lens; 
      label = label_lens
  }
  
  (* Record 2 *)
  type myrecord2 = {
      foo : int;
      buz : string;
      label : string
  }
  let myrecord2 = {
      foo = 4;
      buz = "five";
      label = "myrecord2"
  }
  let foo_lens = { 
      get = (fun x -> x.foo); 
      set = (fun v x -> { x with foo = v })
  }
  let buz_lens = { 
      get = (fun x -> x.buz); 
      set = (fun v x -> { x with buz = v })
  }
  let label_lens = { 
      get = (fun x -> x.label); 
      set = (fun v x -> { x with label = v })
  }
  let myrecord2_lens = {
      foo = foo_lens; 
      label = label_lens
  }
  
  (* Write a generic function to work with these objects*)
  let myfunc (myrecord:'a) (lens:'a myrecord_lens) =
      let _ = Printf.printf "Working with: %s\n" (lens.label.get myrecord) in
      (lens.foo.get myrecord) + 1
  
  (* Get some results *)
  let _ = myfunc myrecord1 myrecord1_lens
  let _ = myfunc myrecord2 myrecord2_lens
For the important pieces of the output, we have:

  val myfunc : 'a -> 'a myrecord_lens -> int = <fun>
  Working with: myrecord1
  - : int = 2
  Working with: myrecord2
  - : int = 5
Hence, by using lenses, we can basically get the polymorphic behavior that we need out of immutable records. The syntax is pretty verbose, but it can be lessened with macros.

As a final note, there is an OCaml lens library to help with this, but you're still mostly stuck writing lenses by hand:

https://github.com/avsm/ocaml-lens

Also, Jane Street has fieldslib, which does some interesting things with record types. It doesn't have exactly what I described above, but it does help in many cases and they have some nice macros to help generate getters and setters:

https://github.com/janestreet/fieldslib

Re:OCaml: Objects by tel on Monday March 16, 2015 @02:55PM (#9213482)
I agree that basic (non van Laarhoven) lenses are pretty achievable in OCaml, though I tend to forget about them.

Truly what you want is the "pre-applied" lens or the store comonad

    type ('i, 'j) store = { get : 'j; set : 'j -> 'i };;
Then object types can have something like

    let o = object
      val slot = 3
      method slot = 
        { get = slot
        ; set = fun slot' -> {< slot = slot' >}
        }
    end
which has type

    val o : < slot : ('a, int) store > as 'a
which is kind of a cool pattern.

    # o#slot.get;;
    - : int = 3
    # (o#slot.set 10)#slot.get;;
    - : int = 10
    # o#slot.set o#slot.get;;
    - : < slot : ('a, int) store > as 'a = <obj>
Re:OCaml: Objects by kxyvr on Monday March 16, 2015 @03:34PM (#9213777)
That is an interesting pattern that essentially does what I need and is much easier to macro expand. Nice job on the pattern!
Re:OCaml: Objects by tel on Monday March 16, 2015 @03:45PM (#9213866)
To be fair, this is completely stolen from the Lens literature in Haskell :)