문제

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.

다른 팁

콘솔 응용 프로그램은 System.windows.forms.dll에 대한 참조를 자동으로 추가하지 않습니다.

솔루션 탐색기에서 프로젝트를 마우스 오른쪽 단추로 클릭하고 참조 추가 ...를 선택한 다음 System.Windows.Forms를 찾아 추가하십시오.

I'm generally unsatisfied with writing code like this

A sign of good taste, if you ask me :-)


I know of no magic, but I think the best path is to split the boilerplate:

  1. One boilerplate setter function for each mutable field. May be useful in different contexts.

  2. One data structure to map integer codes to "what to do with this field"

You can implement your record scanner using a table instead of a function. A suggestive example appears below. The difference between gets and gett is a real kicker here. In what follows,

  • sf stands for "string field"
  • tf stands for "time field"
  • eor stands for "end of record"

I've made up tabulate and lookup to suit my example; use whatever data structure is efficient.

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
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top