File: README

package info (click to toggle)
fieldslib 0.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 188 kB
  • ctags: 101
  • sloc: ml: 349; makefile: 63
file content (150 lines) | stat: -rw-r--r-- 4,898 bytes parent folder | download
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.
...