-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathmlist.ml
170 lines (137 loc) · 7.28 KB
/
mlist.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
(* This is just a demonstration, in the simple case of lists, of a technique for unifying tranversals of data structures. We use it later in more complicated cases of Bwvs, Cubes, and Tubes. *)
open Tlist
open Hlist
module Heter = struct
(* An hlist of lists *)
type _ ht = [] : nil ht | ( :: ) : 'x list * 'xs ht -> ('x, 'xs) cons ht
(* The hlist consisting of all empty lists *)
let rec empty : type xs. xs Tlist.t -> xs ht = function
| Nil -> []
| Cons xs -> [] :: empty xs
(* Add an element to the front of each one of an hlist of lists *)
let rec cons : type xs. xs hlist -> xs ht -> xs ht =
fun x xs ->
match (x, xs) with
| [], [] -> []
| x :: xs, y :: ys -> (x :: y) :: cons xs ys
(* Extract the head and tail of element of every one of an hlist of lists *)
let rec hd : type xs. xs ht -> xs hlist = function
| [] -> []
| x :: xs -> List.hd x :: hd xs
let rec tl : type xs. xs ht -> xs ht = function
| [] -> []
| x :: xs -> List.tl x :: tl xs
(* Every hlist of lists has a valid type *)
let rec tlist : type xs. xs ht -> xs Tlist.t = function
| [] -> Nil
| _ :: xs -> Cons (tlist xs)
end
(* Now we define a single traversal function which encapsulates map, map2, map3, ..., iter, iter2, ... mapM, mapM2, ..., iterM, iterM2, ... and also multiple-output versions and left and right folds. It takes a nonempty hlist of lists as input and produces an hlist of lists in some applicative functor as output, where the traversal function acts on hlists of elements. Applicative functors are the natural level of generality for this, and are needed for some esoteric applications like right-fold with simultaneous map, but later we specialize it to monads which include most examples. *)
module Applicatic (M : Applicative.Plain) = struct
open Applicative.Ops (M)
let rec pmapM :
type x xs ys.
((x, xs) cons hlist -> ys hlist M.t) -> (x, xs) cons Heter.ht -> ys Tlist.t -> ys Heter.ht M.t
=
fun f xss ys ->
match xss with
| [] :: _ -> return (Heter.empty ys)
| (x :: xs) :: xss ->
let+ fx = f (x :: Heter.hd xss) and+ fxs = pmapM f (xs :: Heter.tl xss) ys in
Heter.cons fx fxs
(* Since "nil hlist" is isomorphic to unit, this includes iterations, but it's more convenient to write those actually using unit. *)
let miterM : type x xs. ((x, xs) cons hlist -> unit M.t) -> (x, xs) cons Heter.ht -> unit M.t =
fun f xss ->
let+ [] =
pmapM
(fun x ->
let+ () = f x in
[])
xss Nil in
()
(* It's also convenient to separate out the multi-input single-output version. *)
let mmapM : type x xs y. ((x, xs) cons hlist -> y M.t) -> (x, xs) cons Heter.ht -> y list M.t =
fun f xs ->
let+ [ ys ] =
pmapM
(fun x ->
let+ y = f x in
[ y ])
xs (Cons Nil) in
ys
end
module Monadic (M : Monad.Plain) = struct
module A = Applicative.OfMonad (M)
include Applicatic (A)
end
(* The non-monadic versions just specialize to the identity. *)
let pmap :
type x xs ys.
((x, xs) cons hlist -> ys hlist) -> (x, xs) cons Heter.ht -> ys Tlist.t -> ys Heter.ht =
fun f xss ys ->
let open Monadic (Monad.Identity) in
pmapM f xss ys
let miter : type x xs. ((x, xs) cons hlist -> unit) -> (x, xs) cons Heter.ht -> unit =
fun f xss ->
let open Monadic (Monad.Identity) in
miterM f xss
let mmap : type x xs y. ((x, xs) cons hlist -> y) -> (x, xs) cons Heter.ht -> y list =
fun f xs ->
let open Monadic (Monad.Identity) in
mmapM f xs
(* Ordinary ones of fixed arity are then obtained by specializing to different kinds of hlists. Note that with hlists, the type determines how many elements it has, so a match against a list of fixed length is exhaustive. Note that the definitions of these are so simple that the user can easily write them directly, and the same is true for the monadic versions mapM, mapM2, etc. In practice, I actually prefer that, so the user has a standard syntax. *)
module Examples = struct
let map : type x y. (x -> y) -> x list -> y list = fun f xs -> mmap (fun [ x ] -> f x) [ xs ]
let map2 : type x y z. (x -> y -> z) -> x list -> y list -> z list =
fun f xs ys -> mmap (fun [ x; y ] -> f x y) [ xs; ys ]
let iter : type x. (x -> unit) -> x list -> unit = fun f xs -> miter (fun [ x ] -> f x) [ xs ]
let iter2 : type x y. (x -> y -> unit) -> x list -> y list -> unit =
fun f xs ys -> miter (fun [ x; y ] -> f x y) [ xs; ys ]
(* Not only does this save copy-and-pasting, especially for data structures whose traversal is complicated to code, it also allows the user to instantiate higher-arity versions as needed by calling mmap directly. It also includes multiple-output versions that are less commonly mentioned, with a bit of mediation between hlists and tuples: *)
let map1_2 : type x y z. (x -> y * z) -> x list -> y list * z list =
fun f xs ->
let [ ys; zs ] =
pmap
(fun [ x ] ->
let y, z = f x in
[ y; z ])
[ xs ] (Cons (Cons Nil)) in
(ys, zs)
(* The same is true for the monadic versions. Moreover, the monadic iteration also incorporates left and right folds, by instantiating to a state monad for left folds, and a reverse state applicative functor (or a continuation monad) for right folds. Again, in practice this is unnecessary; the user can just instantiate to the appropriate monad directly. *)
let mfold_left :
type x xs acc. (acc -> (x, xs) cons hlist -> acc) -> acc -> (x, xs) cons Heter.ht -> acc =
fun f acc xss ->
let open Monadic (Monad.State (struct
type t = acc
end)) in
snd (miterM (fun xs s -> ((), f s xs)) xss acc)
let fold_left : type x acc. (acc -> x -> acc) -> acc -> x list -> acc =
fun f acc xs -> mfold_left (fun acc [ x ] -> f acc x) acc [ xs ]
let fold_left2 : type x y acc. (acc -> x -> y -> acc) -> acc -> x list -> y list -> acc =
fun f acc xs ys -> mfold_left (fun acc [ x; y ] -> f acc x y) acc [ xs; ys ]
let mfold_right :
type x xs acc. ((x, xs) cons hlist -> acc -> acc) -> (x, xs) cons Heter.ht -> acc -> acc =
fun f xss acc ->
let open Applicatic (Applicative.RevState (struct
type t = acc
end)) in
snd (miterM (fun xs s -> ((), f xs s)) xss acc)
let mfold_right' :
type x xs acc. ((x, xs) cons hlist -> acc -> acc) -> (x, xs) cons Heter.ht -> acc -> acc =
fun f xss acc ->
let open Monadic (Monad.Cont (struct
type t = acc
end)) in
miterM (fun xs cont -> f xs (cont ())) xss (fun () -> acc)
let fold_right : type x acc. (x -> acc -> acc) -> x list -> acc -> acc =
fun f xs acc -> mfold_right (fun [ x ] acc -> f x acc) [ xs ] acc
let fold_right2 : type x y acc. (x -> y -> acc -> acc) -> x list -> y list -> acc -> acc =
fun f xs ys acc -> mfold_right (fun [ x; y ] acc -> f x y acc) [ xs; ys ] acc
(* The reverse-state version of this has the additional advantage that, like the forwards state version, it can be combined with a simultaneous map. Thus, the generic traversal parametrized over an applicative functor allows the caller to specify the *direction* of traversal. *)
let fold_right_map : type x y acc. (x -> acc -> y * acc) -> x list -> acc -> y list * acc =
fun f xss acc ->
let open Applicatic (Applicative.RevState (struct
type t = acc
end)) in
mmapM (fun [ xs ] s -> f xs s) [ xss ] acc
end