package inferno
A library for constraint-based Hindley-Milner type inference
Install
dune-project
Dependency
Authors
Maintainers
Sources
archive.tar.gz
md5=cf37ba58410ca1e5e5462d51e4c4fb46
sha512=f96ad6bbf99482455afd8e8a9503357f21798698e6a2a4a8d385877db844ffebcef24f8cf82622c931831896088a9b98e37f4230839a3d03ec1c64fae2a39920
doc/src/inferno/Decoder.ml.html
Source file Decoder.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 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
(******************************************************************************) (* *) (* Inferno *) (* *) (* François Pottier, Inria Paris *) (* *) (* Copyright Inria. All rights reserved. This file is distributed under the *) (* terms of the MIT License, as described in the file LICENSE. *) (* *) (******************************************************************************) open Signatures module Make (S : OSTRUCTURE) (U : MUNIFIER with type 'a structure := 'a S.structure) (O : OUTPUT with type 'a structure := 'a S.structure) = struct open U (* -------------------------------------------------------------------------- *) (* Decoding a variable. *) (* The public function [decode_variable] decodes a variable, which must have leaf structure. The decoded variable is determined by the unique identity of the variable [v]. *) let decode_variable (v : variable) : O.tyvar = let data = U.get v in assert (S.is_leaf data); O.inject (S.id data) (* The internal function [decode_id] decodes a variable. As above, this variable must have leaf structure, and the decoded variable is determined by the unique identity [id], which is already at hand. *) (* [O.variable] is used to convert the result from [O.tyvar] to [O.ty]. *) let decode_id (id : id) : O.ty = O.variable (O.inject id) (* -------------------------------------------------------------------------- *) (* An acyclic decoder performs a bottom-up computation over an acyclic graph. *) let new_acyclic_decoder () : variable -> O.ty = (* This hash table records the equivalence classes that have been visited and the value that has been computed for them. *) let visited : (id, O.ty) Hashtbl.t = Hashtbl.create 128 in let rec decode (v : variable) : O.ty = let data = get v in let id = S.id data in try Hashtbl.find visited id with Not_found -> (* Because the graph is assumed to be acyclic, it is ok to update the hash table only after the recursive call. *) let a = if S.is_leaf data then decode_id id else O.structure (S.map decode data) in Hashtbl.add visited id a; a in decode (* -------------------------------------------------------------------------- *) (* The cyclic decoder is designed so as to be able to construct recursive types using μ syntax. We must ensure that every use of a μ-bound variable is dominated by its binder. *) (* One cannot naively use a table of visited nodes, which maps every node to a decoded value (as done in the acyclic decoder above). Indeed, when an already-visited cycle is re-entered via a different path, we would risk producing a decoded value that contain variables that are meant to be μ-bound, but are actually unbound, because the μ binder lies along a different path. *) (* Another naive approach would be to perform no memoization at all. This can work, but can be exponentially inefficient, because a DAG is decoded to a tree. *) (* We choose to use a memoization table, [visited], but we memoize only the vertices that do not participate in a cycle. In practice, we expect cycles to be infrequent, so this approach should have good complexity. *) (* The long-lived hash table [visited] maps the vertices that have been decoded already, and that do not participate in a cycle, to a decoded value. *) (* When are asked to decode a vertex [v] that has not been memoized yet, we explore the graph formed by the vertices that are reachable from [v] and have not yet been memoized. We compute the strongly connected components of this graph. At a component of size 1, we memoize the decoded value. At a component of size greater than 1, we do not memoize it. The decoding process remains top-down, because (as far as I can tell) only a top-down process can tell us where μ binders must be placed. *) (* -------------------------------------------------------------------------- *) (* [isolated visited root] computes which unvisited vertices are reachable from [root]; computes the strongly connected components of this graph; and returns a function [isolated] of type [data -> bool] that indicates whether a vertex is the sole inhabitant of its component. *) let isolated visited (root : variable) : data -> bool = (* A depth-first search lets us discover the unvisited vertices that are reachable from [root]. At the same time, we count them, and assign a unique index to each of them. *) let n = ref 0 in let discovered : (id, int) Hashtbl.t = Hashtbl.create 16 in let inhabitants : data list ref = ref [] in let rec discover v = let data = get v in let id = S.id data in if not (Hashtbl.mem visited id || Hashtbl.mem discovered id) then begin Hashtbl.add discovered id (Utils.postincrement n); inhabitants := data :: !inhabitants; S.iter discover data end in discover root; (* Then, we compute the strongly connected components of this graph. *) let module G = struct type node = data let n = !n let index data = try Hashtbl.find discovered (S.id data) with Not_found -> assert false let successors yield data = data |> S.iter (fun v -> let data = get v in if Hashtbl.mem discovered (S.id data) then yield data ) let iter yield = List.iter yield !inhabitants end in let module T = Tarjan.Run(G) in T.isolated (* -------------------------------------------------------------------------- *) (* The short-lived hash table [table] (which is empty when the decoder is inactive) records which vertices are the target of a back edge and therefore need a μ binder. A vertex is mapped to [Active] when it is being visited. It is mapped to [Rediscovered] when it is being visited and a back edge to it has been discovered. This indicates that this vertex needs a μ binder. *) type status = | Active | Rediscovered (* -------------------------------------------------------------------------- *) (* The cyclic decoder. *) let new_cyclic_decoder () : variable -> O.ty = (* The long-lived table. *) let visited : (id, O.ty) Hashtbl.t = Hashtbl.create 128 in (* The short-lived table. *) let table : (id, status) Hashtbl.t = Hashtbl.create 128 in (* The toplevel decoding function. *) let decode (root : variable) : O.ty = (* If [root] appears in the table [visited], we are done. *) let data = get root in try Hashtbl.find visited (S.id data) with Not_found -> (* Otherwise, discover the unvisited vertices that are reachable from [root], and determine which of them are isolated, that is, which of them lie in a component of size 1. *) let isolated = isolated visited root in (* Then, the following recursive decoding function is used. The short-lived table [table] is used to detect back edges and determine where μ binders must be placed. *) let rec decode (v : variable) : O.ty = (* If [v] appears in the table [visited], we are done. *) let data = get v in let id = S.id data in try Hashtbl.find visited id with Not_found -> (* Otherwise, compute a decoded value [a] for this vertex. *) let a = if S.is_leaf data then (* There is no structure. *) decode_id id else (* There is some structure. *) if Hashtbl.mem table id then begin (* We have just rediscovered this vertex via a back edge. Its status may be [Active] or [Rediscovered]. We change it to [Rediscovered], and stop the traversal. *) Hashtbl.replace table id Rediscovered; decode_id id end else begin (* This vertex is not being visited. Before the recursive call, mark it as being visited. *) Hashtbl.add table id Active; (* Perform the recursive traversal. *) let a = O.structure (S.map decode data) in (* Mark this vertex as no longer being visited. If it was recursively rediscovered during the recursive call, introduce a μ binder. (It would be correct but noisy to always introduce a μ binder.) *) let status = try Hashtbl.find table id with Not_found -> assert false in Hashtbl.remove table id; match status with | Active -> a | Rediscovered -> O.mu (O.inject id) a end in (* If this vertex is isolated, store the decoded value [a] in the memoization table [visited]. (It would be correct to never memoize. It would be incorrect to always memoize.) *) if isolated data then Hashtbl.add visited id a; a in assert (Hashtbl.length table = 0); let a = decode root in assert (Hashtbl.length table = 0); a in decode (* -------------------------------------------------------------------------- *) end (* Make *)
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>