4 ms·
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
by tel 12y ago
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...
- kxyvr 12y agoIn 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 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 https://github.com/janestreet/fieldslib
- tel 12y agoI 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>