Source file type_distance.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
type step =
| Wildcard
| Tyname of string
| Tyvar of int
| Left_arrow
| Right_arrow
| Product of { position : int; length : int }
| Argument of { position : int; length : int }
module P = Type_polarity
let make_path t =
let rec aux prefix = function
| Type_expr.Unhandled -> []
| Type_expr.Wildcard -> [ Wildcard :: prefix ]
| Type_expr.Tyvar x -> [ Tyvar x :: prefix ]
| Type_expr.Arrow (a, b) ->
List.rev_append
(aux (Left_arrow :: prefix) a)
(aux (Right_arrow :: prefix) b)
| Type_expr.Tycon (constr, []) -> [ Tyname constr :: prefix ]
| Type_expr.Tycon (constr, args) ->
let length = String.length constr in
let prefix = Tyname constr :: prefix in
args
|> List.mapi (fun position arg ->
let prefix = Argument { position; length } :: prefix in
aux prefix arg)
|> List.fold_left (fun acc xs -> List.rev_append xs acc) []
| Type_expr.Tuple args ->
let length = List.length args in
args
|> List.mapi (fun position arg ->
let prefix = Product { position; length } :: prefix in
aux prefix arg)
|> List.fold_left (fun acc xs -> List.rev_append xs acc) []
in
List.map List.rev (aux [] t)
let make_cache xs ys =
let h = List.length xs |> succ
and w = List.length ys |> succ
and not_used = -1 in
Array.make_matrix h w not_used
let skip_entry = 10
let max_distance = 10_000
let distance xs ys =
let cache = make_cache xs ys in
let rec memo ~xpolarity ~ypolarity i j xs ys =
let cell = cache.(i).(j) in
if cell >= 0 then cell
else
let value = aux ~xpolarity ~ypolarity i j xs ys in
let () = cache.(i).(j) <- value in
value
and aux ~xpolarity ~ypolarity i j xs ys =
match (xs, ys) with
| [], _ -> 0
| [ Wildcard ], _ -> 0
| _, [] -> max_distance
| [ Tyvar _ ], [ Wildcard ] when P.equal xpolarity ypolarity -> 0
| [ Tyvar x ], [ Tyvar y ] when P.equal xpolarity ypolarity ->
if Int.equal x y then 0 else 1
| Left_arrow :: xs, Left_arrow :: ys ->
let xpolarity = P.negate xpolarity and ypolarity = P.negate ypolarity in
memo ~xpolarity ~ypolarity (succ i) (succ j) xs ys
| Left_arrow :: xs, _ ->
let xpolarity = P.negate xpolarity in
memo ~xpolarity ~ypolarity (succ i) j xs ys
| _, Left_arrow :: ys ->
let ypolarity = P.negate ypolarity in
memo ~xpolarity ~ypolarity i (succ j) xs ys
| _, Right_arrow :: ys -> memo ~xpolarity ~ypolarity i (succ j) xs ys
| Right_arrow :: xs, _ -> memo ~xpolarity ~ypolarity (succ i) j xs ys
| Product { length = a; _ } :: xs, Product { length = b; _ } :: ys
| Argument { length = a; _ } :: xs, Argument { length = b; _ } :: ys ->
let l = abs (a - b) in
l + memo ~xpolarity ~ypolarity (succ i) (succ j) xs ys
| Product _ :: xs, ys -> 1 + memo ~xpolarity ~ypolarity (succ i) j xs ys
| xs, Product _ :: ys -> 1 + memo ~xpolarity ~ypolarity i (succ j) xs ys
| Tyname x :: xs', Tyname y :: ys' when P.equal xpolarity ypolarity -> (
match Name_cost.distance x y with
| None -> skip_entry + memo ~xpolarity ~ypolarity i (succ j) xs ys'
| Some cost -> cost + memo ~xpolarity ~ypolarity (succ i) (succ j) xs' ys'
)
| xs, Tyname _ :: ys ->
skip_entry + memo ~xpolarity ~ypolarity i (succ j) xs ys
| xs, Argument _ :: ys -> memo ~xpolarity ~ypolarity i (succ j) xs ys
| _, (Wildcard | Tyvar _) :: _ -> max_distance
in
let positive = P.positive in
aux ~xpolarity:positive ~ypolarity:positive 0 0 xs ys
let make_array list =
list |> Array.of_list
|> Array.map (fun li ->
let li = List.mapi (fun i x -> (x, i)) li in
List.sort Stdlib.compare li)
let init_heuristic list =
let used = Array.make List.(length @@ hd list) false in
let arr = make_array list in
let h = Array.make (succ @@ Array.length arr) 0 in
let () = Array.sort Stdlib.compare arr in
let () =
for i = Array.length h - 2 downto 0 do
let best = fst @@ List.hd arr.(i) in
h.(i) <- h.(i + 1) + best
done
in
(used, arr, h)
let replace_score best score = best := Int.min score !best
let minimize = function
| [] -> 0
| list ->
let used, arr, heuristics = init_heuristic list in
let best = ref 1000 and limit = ref 0 in
let len_a = Array.length arr in
let rec aux rem acc i =
let () = incr limit in
if !limit > max_distance then false
else if rem <= 0 then
let score = acc + (1000 * (len_a - i)) in
let () = replace_score best score in
true
else if i >= len_a then
let score = acc + (5 * rem) in
let () = replace_score best score in
true
else if acc + heuristics.(i) >= !best then true
else
let rec find = function
| [] -> true
| (cost, j) :: rest ->
let continue =
if used.(j) then true
else
let () = used.(j) <- true in
let continue = aux (pred rem) (acc + cost) (succ i) in
let () = used.(j) <- false in
continue
in
if continue then find rest else false
in
find arr.(i)
in
let _ = aux (Array.length used) 0 0 in
!best
let compute ~query ~entry =
let query = make_path query in
let path = make_path entry in
match (path, query) with
| _, [] | [], _ -> 1000
| _ -> query |> List.map (fun p -> List.map (distance p) path) |> minimize