I find that records often result in redundant code... the same operation being specified for different fields (of the same type). I could use arrays in these cases, but then lose meaningful (to programmers) field names.

I tried using camlp4 (and camlp5) macros to get the effect of passing a field "name" to a "function". I could get my example to work if I update mutable fields (record.field <- x), but using the functional record-update {record with field=x} doesn't work...


--- Simplified mock-up of my current situation (not enough to compile) ---

(* a record with some fields of the same type... imagine there might be many more fields *)
type stats = { strength: int; agility: int }

(* macro which doesn't work... "field" which follows "with" doesn't get replaced *)
DEFINE AGE(field) = fun (state,age) ->
  let s,a = age_stat state.field age.field in
  {state with field=s}, {age with field=a}

(* val f : (stats * stats) -> int -> (stats * stats) *)
let age_character input = function
  | n when n < 2 -> input
  | 2 -> AGE(strength) input
  | 3 -> AGE(agility) input
  | _ -> AGE2(strength,agility) input

(* a mock-up of usage... *)
let state = { strength=3; agility=1 } in
let age = { strength=0; agility=0 } in
let state',age' = age_character (state,age) (rand 8) in ...

---

After processing by camlp4 with macros this is what the first case becomes:

  | 1 -> let s,a = age_stat state.strength age.strength in
         {state with field=s}, {age with field=a}

"field" isn't replaced with "strength" in the record-update. I tried looking at Camlp4MacroParser.ml... but it makes my head swim. It must be doing something much more careful than literal text replacement, but perhaps too careful... or incomplete? Does anyone know how these macros work? Is this proper behavior for some reason, or an unhandled edge case?

The problem is I don't want a block of code like this to be repeated 10 times with only a field-name change for each (and 4 field names each time!). That's not well readable, prone to error, and harder to maintain properly.

Sometimes I wish I could present an alternative view of the same data, such as having an array "view" into part of a record... verified to be typesafe by the compiler... and compiled into the same simple offsets in the end. Maybe it's my asm/C origins which I never seem to escape. I mention this in hope that someone says "Oh, that's exactly what you can do... like this!" :)

Thank-you for any help caml-list!

 Tony