Source file profunctor_intf.ml
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
open Base
module Interfaces = struct
(** A profunctor has an input end with contravariant map, an output
end with covariant map and an operation to join two terms with
the same input type giving both outputs.
*)
module type S = sig
type ('b, 'a) t
val map : ('a, 'u) t -> f:('a -> 'b) -> ('b, 'u) t
val contra_map : ('u, 'b) t -> f:('a -> 'b) -> ('u, 'a) t
val both : ('a, 'i) t -> ('b, 'i) t -> ('a * 'b, 'i) t
end
(** @open *)
(** A module used to traverse each field of a record performing some
action using a specific profunctor.
You can construct a record builder for a given profunctor using
{{!module-Profunctor.module-Record_builder}Record_builder}.
This is based on the library {!module-Record_builder}
(which provides traversal with an
{{!modtype:Base.Applicative.S}applicative}) and adds
the contravariant mapping of each field.
{[
type t =
{ name : string
; age : int
}
[@@deriving fields ~iterators:make_creator]
module F : Profunctor.S = "your module here"
module F_of_record = Profunctor.Record_builder (F)
let for_string : (string, string) F.t = "your code here"
let for_int : (int, int) F.t = "your code here"
let example : (t, t) F.t =
F_of_record.(
build_for_record
(Fields.make_creator ~name:(field for_string) ~age:(field for_int)))
;;
]}
This is equivalent to:
{[
let example : (t, t) F.t =
let name = F.contra_map ~f:name for_string
and age = F.contra_map ~f:age for_int in
F.map (F.both name age) ~f:(fun (name, age) -> { name; age })
;;
]}
*)
module type Record_builder = sig
type ('b, 'a) profunctor
(** A term of the profunctor where the input and output type are
the same.
Although this module must use the type parameters separately
internally all terms supplied as arguments or returned will
have both type parameters equal. This type alias is used to
allow automatically converting with some other type in some
cases e.g. {!module-Of_conv_based}.
*)
type 'a profunctor_term
val prj : ('a, 'a) profunctor -> 'a profunctor_term
val inj : 'a profunctor_term -> ('a, 'a) profunctor
(** The underlying applicative record builder,
which does not perform the contravariant mapping.
*)
module Bare : Record_builder.S2 with type ('b, 'a) applicative = ('b, 'a) profunctor
(** Supply the term for one field.
The type of this function is designed to match up with [Fields.make_creator]
(see the example).
*)
val field
: 'field profunctor_term
-> ('record, 'field) Field.t
-> ('field, _, _, 'record) Bare.Make_creator_types.handle_one_field
(** Build the overarching profunctor for the whole record.
This takes a partial application of [Fields.make_creator] as its argument,
which should supply no initial value but use {!field} to supply a term
for every field of the record.
The type of this is designed to match up with [Fields.make_creator]
(see the example).
*)
val build_for_record
: ('record, _, 'record) Bare.Make_creator_types.handle_all_fields
-> 'record profunctor_term
end
(** A profunctor constructed from an {{!modtype:Base.Applicative.S}applicative}.
In this case [contra_map] has no effect, and [map] and [both]
behave exactly as they would with the underlying applicative.
*)
module type Of_applicative = sig
type 'a applicative
type ('b, 'a) t = 'b applicative
include S with type ('b, 'a) t := ('b, 'a) t
module Of_record :
Record_builder
with type 'a profunctor_term = 'a applicative
and type ('b, 'a) profunctor = ('b, 'a) t
end
(** A profunctor-ish where both parameters must be mapped together
at the same time. This is less expressive but appears in
several libraries.
*)
module type Conv_based = sig
type 'a t
val conv : 'a t -> ('a -> 'b) -> ('b -> 'a) -> 'b t
val both : 'a t -> 'b t -> ('a * 'b) t
end
(** Embed a {!modtype:Conv_based} profunctor-ish into a full profunctor,
allowing the use of {!modtype:Record_builder} directly.
*)
module type Of_conv_based = sig
type 'a conv_based
type ('b, 'a) t
include S with type ('b, 'a) t := ('b, 'a) t
val inj : 'a conv_based -> ('a, 'a) t
val prj : ('a, 'a) t -> 'a conv_based
module Of_record :
Record_builder
with type 'a profunctor_term = 'a conv_based
and type ('b, 'a) profunctor = ('b, 'a) t
end
end
module type Profunctor = sig
include module type of Interfaces (** @inline *)
module Record_builder (F : S) :
Record_builder
with type ('b, 'a) profunctor = ('b, 'a) F.t
and type 'a profunctor_term = ('a, 'a) F.t
module Of_applicative (F : Applicative.S) :
Of_applicative with type 'a applicative := 'a F.t
module Of_conv_based (F : Conv_based) : Of_conv_based with type 'a conv_based := 'a F.t
(** A profunctor which represents a function where
[Fn.id] may sometimes be distinguished from other
functions.
This is primarily used internally to implement other
things where discarding identity functions can be advantageous.
*)
module Fn_with_id : sig
type ('b, 'a) t =
| Id : ('a, 'a) t
| Apply : ('a -> 'b) -> ('b, 'a) t
include S with type ('b, 'a) t := ('b, 'a) t
val id : ('a, 'a) t
val of_fn : ('a -> 'b) -> ('b, 'a) t
(** Unpack the function, [as_fn (of_fn x) ≡ Staged.stage x]. *)
val as_fn : ('b, 'a) t -> ('a -> 'b) Staged.t
(** [split l r ≡ both (contra_map ~f:fst l) (contra_map ~f:snd r)],
but is more efficient (the result may be [Id]).
*)
val split : ('b, 'a) t -> ('d, 'c) t -> ('b * 'd, 'a * 'c) t
(** Composition of function, [as_fn (compose g f) ≡ Fn.compose (as_fn g) (as_fn f)]. *)
val compose : ('c, 'b) t -> ('b, 'a) t -> ('c, 'a) t
module Of_record :
Record_builder
with type ('b, 'a) profunctor = ('b, 'a) t
and type 'a profunctor_term = ('a, 'a) t
end
end