News for hackers. Stuff that matters.
Re:OCaml: Objects by kxyvr on Monday March 16, 2015 @12:53PM (#9212606)
What I never understood is why OCaml allows row polymorphism in objects, but not in record types. For example, we can write:

  let bar x = x#info + 1
and OCaml will give a type:

  val bar : < info : int; .. > -> int = <fun>
which is precisely what we want since we can now define functions on any object that has an info member. At the same time, we can't write

  let buz x = x.info + 1
because we receive the error

  Error: Unbound record field info
This is a big deal since the exposed members of objects must be methods in OCaml. Certainly, we can declare values and mutable values, but these are not exposed in the objects type. Specifically,

  # class foo = object val info = 1 end;;
  class foo : object val info : int end
  # let bar = new foo;;
  val bar : foo = <obj>
  # bar#info;;
  Error: This expression has type foo
         It has no method info
In order to get around this, we can change these values to methods (or add getter methods that return these values). However, the problem with doing that is then we can't make a new object with all of the same values, but with the specified valued changed:

  # class foo = object method info = 1 end;;
  class foo : object method info : int end
  # let bar = new foo;;
  val bar : foo = <obj>
  # bar # info;;
  - : int = 1
  # let buz = bar with info = 2;;
  Error: Parse error: [str_item] or ";;" expected (in [top_phrase])
In other words, we don't have a good mechanism for creating a bunch of immutable objects.

In other words, OCaml records allow immutable records, but they lack row polymorphsim. OCaml classes allow row polymorphism, but disallow immutable updates (immutable updates meaning create a new object with everything the same, but one element different.)

Does anyone know why this is the case?

Re:OCaml: Objects by rwmj on Monday March 16, 2015 @01:14PM (#9212761)
I believe it's because #info (ie. method access) is kind of dynamic. The string "info" is hashed and then the hash is looked up at runtime against the class vtable. (For precise details, see 19.3.5 here: http://caml.inria.fr/pub/docs/manual-ocaml/intfc.html) I'm not clear under what circumstances this can be optimized to a direct call, and there is also some kind of cache, so repeated method calls to the same method are fast. This is the reason people who value performance over everything else steer clear of using objects in OCaml.

Record fields are simply offsets in a struct, so the type must be fully known when compiling the code. It would not be possible to compile a function that could work on two different types of struct that both happened to have a .info field.

Re:OCaml: Objects by kxyvr on Monday March 16, 2015 @02:22PM (#9213250)
Actually, that makes a lot of sense. Thanks for the insight.
Re:OCaml: Objects by kopter on Monday March 16, 2015 @01:52PM (#9213044)
Roughly, the intuition is that in OCaml, objects are to records as polymorphic variants are to regular variants
Re:OCaml: Objects by emillon on Monday March 16, 2015 @02:50PM (#9213448)
> OCaml classes allow row polymorphism, but disallow immutable updates (immutable updates meaning create a new object with everything the same, but one element different.)

There's a functional update construct for objects:

http://caml.inria.fr/pub/docs/manual-ocaml/objectexamples.ht...

It works like { r with field = value }.

Re:OCaml: Objects by tel on Monday March 16, 2015 @02:58PM (#9213496)
That syntax works for records not objects. The {< _ = _ >} syntax is sort-of equivalent except it can only be used within method definitions (!) and is therefore too restricted.

Instead, try taking a look at the store-comonad mechanism I've got below: https://news.ycombinator.com/item?id=9213482

Re:OCaml: Objects by tel on Monday March 16, 2015 @01:17PM (#9212786)
This would be a great place to include lenses if OCaml allowed for higher rank polymorphism. I don't think you can jerry-rig a Functor-based lens solution into an object like this...
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 :)