4 ms·
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 th
by kxyvr 12y ago
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 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>