I'm generally unsatisfied with writing code like this:
let load_record_field cursor gets geti gett a = function
| 0x01 -> let c, s = gets () in (a.a_record_uuid <- s; `More_record c)
| 0x02 -> let c, s = gets () in (a.a_group <- s; `More_record c)
| 0x03 -> let c, s = gets () in (a.a_title <- s; `More_record c)
| 0x04 -> let c, s = gets () in (a.a_username <- s; `More_record c)
| 0x07 -> let c, t = gett () in (a.a_creation_time <- t; `More_record c)
.
.
.
| 0xFF -> `End_of_record cursor
I've minimized the boilerplate, but I was wondering if there was any OCaml magic that would let me completely eliminate it.
This is dead simple: just use a closure to do the setting, and write a function to abstract out the boilerplate
let load_record_field cursor gets geti gett a x =
let frob get set =
let (c,s) = get () in
set s; `More_record c
in
function
| 0x01 -> frob gets (fun s -> a.a_record_uuid <- s)
| 0x02 -> frob gets (fun s -> a.a_group <- s)
| 0x03 -> frob gett (fun s -> a.a_title <- s)
...
and so on.
You can make this even better if you use a macro package like Jane Street's fieldslib. That generates first-class fields, along with automatically generated setters and getters. This would mean that you wouldn't have to construct the closure each time by hand.
The shortest you could get away with in theory is:
frobnicate (function
| 0x01 -> gets , a_record_uuid
| 0x02 -> gets , a_group
...
)
Of course, you will be foiled by OCaml because 1° there are no "pointer to member" constructs in Objective Caml, so you would have to write fun a s -> a.a_record_uuid <- s
instead of a_record_uuid
(at the very least) and 2° the type system does not fully support existential quantification, so that the return type of the function cannot be the expected:
exists 'a. int -> (unit -> record * 'a) * ('a -> record -> unit)
I guess you could solve 1° by having named functions for setting values in a record, if you happen to do that often enough:
type complex = { re : int ; im : int }
let re r c = { c with re = r }
let im r c = { c with im = i }
It's a bit unorthodox, I guess, but it usually pays off later on because I tend to use them in most functional situations. You could create the equivalent in imperative style, or you could accept the overhead of a function (it only adds around 20 characters).
As or 2°, it can be solved by hiding the existential quantifier in a function:
let t e read write = let c, x = read () in write x e ; `More_record c
This would let you go down to:
let t = t a in
match
| 0x01 -> t gets a_record_uuid
| 0x02 -> t gets a_title
...
I wouldn't be surprised if CamlP4 supported some kind of sugar for assignment functions. In the mean time, if you use references instead of mutable fields, you can shorten this up (because references are first class values, fields are not):
let t read reference = let c, x = read () in reference := x ; `More_record c
match
| 0x01 -> t gets a.a_record_uuid
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With