1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
|
1 What is "fields"?
*=*=*=*=*=*=*=*=*=*=*
This library defines a syntax extension for OCaml using Camlp4 that
can be used to define first class values representing record fields,
and additional routines, to get and set record fields, iterate and fold
over all fields of a record and create new record values.
2 Basic use
*=*=*=*=*=*=*=*=*=*=*=
<< type t = {
dir : [ `Buy | `Sell ];
quantity : int;
price : float;
mutable cancelled : bool;
} with fields
>>
This generates the following values
<<
(* getters *)
val cancelled : t -> bool
val price : t -> float
val quantity : t -> int
val dir : t -> [ `Buy | `Sell ]
(* setters *)
val set_cancelled : t -> bool -> unit
(* higher order fields and functions over all fields *)
module Fields :
sig
val cancelled : (t, bool) Fieldslib.Field.t
val price : (t, float) Fieldslib.Field.t
val quantity : (t, int) Fieldslib.Field.t
val dir : (t, [ `Buy | `Sell ]) Fieldslib.Field.t
val make_creator :
dir:((t, [ `Buy | `Sell ]) Fieldslib.Field.t ->
'a -> ('b -> [ `Buy | `Sell ]) * 'c) ->
quantity:((t, int) Fieldslib.Field.t -> 'c -> ('b -> int) * 'd) ->
price:((t, float) Fieldslib.Field.t -> 'd -> ('b -> float) * 'e) ->
cancelled:((t, bool) Fieldslib.Field.t -> 'e -> ('b -> bool) * 'f) ->
'a -> ('b -> t) * 'f
val iter :
dir:((t, [ `Buy | `Sell ]) Fieldslib.Field.t -> 'a) ->
quantity:((t, int) Fieldslib.Field.t -> 'b) ->
price:((t, float) Fieldslib.Field.t -> 'c) ->
cancelled:((t, bool) Fieldslib.Field.t -> 'd) -> 'd
val fold :
init:'a ->
dir:('a -> (t, [ `Buy | `Sell ]) Fieldslib.Field.t -> 'b) ->
quantity:('b -> (t, int) Fieldslib.Field.t -> 'c) ->
price:('c -> (t, float) Fieldslib.Field.t -> 'd) ->
cancelled:('d -> (t, bool) Fieldslib.Field.t -> 'e) -> 'e
val map :
dir:((t, [ `Buy | `Sell ]) Fieldslib.Field.t -> [ `Buy | `Sell ]) ->
quantity:((t, int) Fieldslib.Field.t -> int) ->
price:((t, float) Fieldslib.Field.t -> float) ->
cancelled:((t, bool) Fieldslib.Field.t -> bool) -> t
val for_all :
dir:((t, [ `Buy | `Sell ]) Fieldslib.Field.t -> bool) ->
quantity:((t, int) Fieldslib.Field.t -> bool) ->
price:((t, float) Fieldslib.Field.t -> bool) ->
cancelled:((t, bool) Fieldslib.Field.t -> bool) -> bool
val exists :
dir:((t, [ `Buy | `Sell ]) Fieldslib.Field.t -> bool) ->
quantity:((t, int) Fieldslib.Field.t -> bool) ->
price:((t, float) Fieldslib.Field.t -> bool) ->
cancelled:((t, bool) Fieldslib.Field.t -> bool) -> bool
end
>>
2 Higher order fields
*=*=*=*=*=*=*=*=*=*=*=
module Field : sig
type ('record, 'field) t = {
name : string;
setter : ('record -> 'field -> unit) option;
getter : 'record -> 'field;
}
val name : (_, _) t -> string
val get : ('record, 'field) t -> 'record -> 'field
val setter : ('record, 'field) t -> ('record -> 'field -> unit) option
end
3 Functions over all fields
*=*=*=*=*=*=*=*=*=*=*=*=*=*=
These are useful to check exhaustiveness wrt. record fields. For example
if you are writing a custom equality operator to ignore small price
differences:
let ( = ) a b : bool =
let use op = fun field ->
op (Field.get field a) (Field.get field b)
in
let price_equal p1 p2 = abs_float (p1 -. p2) < 0.001 in
Fields.for_all
~dir:(use (=)) ~quantity:(use (=))
~price:(use price_equal) ~cancelled:(use (=))
;;
A type error would occur if you were to add a new field and not change
the definition of ( = ):
type t = {
dir : [ `Buy | `Sell ];
quantity : int;
price : float;
mutable cancelled : bool;
symbol : string;
} with fields
...
Error: This expression has type
symbol:((t, string) Fieldslib.Field.t -> bool) -> bool
but an expression was expected of type
bool
Or similarly you could use fold to create to_string function:
let to_string t =
let conv to_s = fun acc f ->
(sprintf "%s: %s" (Field.name f) (to_s (Field.get f t))) :: acc
in
let fs =
Fields.fold ~init:[]
~dir:(conv (function `Buy -> "Buy" | `Sell -> "Sell"))
~quantity:(conv string_of_int)
~price:(conv string_of_float)
~cancelled:(conv string_of_bool)
in
String.concat fs ~sep:", "
;;
Further functions exist for boolean logic over fields (for_all, exists),
map a set of functions that return the field values into a record (map)
and to iterate over a elements of a record (iter).
4 Staged creation
*=*=*=*=*=*=*=*=*=*=*=
make_creator is designed to write functions that create records.
It is rather powerful, which made the type somewhat convoluted.
...
|