在 OCaml 中分解产品类型分配
-
23-09-2019 - |
题
我通常对编写这样的代码不满意:
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
我已经最小化了样板文件,但我想知道是否有任何 OCaml 魔法可以让我完全消除它。
解决方案
这是死的简单:只使用一个封闭件来执行设置,并写入到抽象的功能出样板
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)
...
等。
您可以更好的让这个,如果你使用宏包像简 华尔街fieldslib。这产生一流的领域,随着 自动生成setter和getter。这意味着你 就不必用手每次构建闭合。
其他提示
理论上你可以逃脱最短的是:
frobnicate (function
| 0x01 -> gets , a_record_uuid
| 0x02 -> gets , a_group
...
)
当然,你会被OCaml中因为1°没有“指针构件”在目的CAML构建挫败,所以你将不得不写入fun a s -> a.a_record_uuid <- s
代替a_record_uuid
(至少是)和2°的类型系统不完全支持存在量化,以便该函数的返回类型不能预期的:
exists 'a. int -> (unit -> record * 'a) * ('a -> record -> unit)
我想你可以通过具有以创纪录的设定值命名的功能解决了1°,如果你碰巧做往往不够:
type complex = { re : int ; im : int }
let re r c = { c with re = r }
let im r c = { c with im = i }
这是一个有点非正统的,我想,但它通常以后得到回报,因为我往往在大多数情况下,功能使用它们。您可以创建在命令行式风格的等效,或者你可以接受一个函数的开销(只增加了大约20个字符)。
As或2°,它可以通过隐藏存在量词在函数解决:
let t e read write = let c, x = read () in write x e ; `More_record c
这会让你去到:
let t = t a in
match
| 0x01 -> t gets a_record_uuid
| 0x02 -> t gets a_title
...
我也不会,如果CamlP4支持某种糖分配功能的惊讶。在此同时,如果你使用的引用,而不是可变域,可以缩短这个了(因为引用是第一类值,字段都没有):
let t read reference = let c, x = read () in reference := x ; `More_record c
match
| 0x01 -> t gets a.a_record_uuid
...
我通常对编写这样的代码不满意
如果你问我的话,这是高品味的标志:-)
我不知道有什么魔法,但我认为最好的方法是拆分样板:
每个可变字段都有一个样板设置器函数。在不同的情况下可能有用。
一种将整数代码映射到“如何处理该字段”的数据结构
您可以使用表而不是函数来实现记录扫描器。下面是一个提示性示例。和...之间的不同 gets
和 gett
这是一个真正的问题。接下来,
sf
代表“字符串字段”tf
代表“时间场”eor
代表“记录结束”
我已经弥补了 tabulate
和 lookup
适合我的例子;使用任何有效的数据结构。
let sf set a c = let c, s = gets() in (set a s; `More_record c)
let tf set a c = let c, s = gett() in (set a t; `More_record c)
let eor a c = `End_of_record c
let fields = tabulate
[ 0x01, sf a_record_uuid
; 0x02, sf a_group
; ...
; 0x07, tf a_creation_time
; ...
]
let load_record_field cursor gets geti gett a code = lookup fields code cursor a