@@ -15,8 +15,18 @@ module Unison.ABT.Normalized
15
15
Align (.. ),
16
16
alpha ,
17
17
freshen ,
18
+ Renaming (.. ),
19
+ isEmptyRenaming ,
20
+ freshenBinder ,
21
+ freshenBinders ,
22
+ pruneRenaming ,
23
+ renameVar ,
24
+ mapping ,
25
+ avoiding ,
26
+ mappingAndAvoiding ,
18
27
renames ,
19
28
renamesAvoiding ,
29
+ renamesAndFreshen0 ,
20
30
rename ,
21
31
transform ,
22
32
visit ,
@@ -26,13 +36,13 @@ where
26
36
27
37
import Data.Bifoldable
28
38
import Data.Bifunctor
29
- import Data.Foldable (toList )
30
39
import Data.Functor.Identity (Identity (.. ))
31
40
import Data.Map.Strict (Map )
32
41
import Data.Map.Strict qualified as Map
33
42
import Data.Maybe (fromMaybe )
34
43
import Data.Set (Set )
35
44
import Data.Set qualified as Set
45
+ import Data.Traversable (mapAccumL )
36
46
import Unison.ABT (Var (.. ))
37
47
38
48
-- ABTs with support for 'normalized' structure where only variables
@@ -107,9 +117,9 @@ class (Bifoldable f, Bifunctor f) => Align f where
107
117
108
118
alphaErr ::
109
119
(Align f ) => (Var v ) => Map v v -> Term f v -> Term f v -> Either (Term f v , Term f v ) a
110
- alphaErr un tml tmr = Left (tml, renamesAndFreshen0 count un tmr)
120
+ alphaErr un tml tmr = Left (tml, renamesAndFreshen0 rn tmr)
111
121
where
112
- count = Map. fromListWith (+) . flip zip [ 1 , 1 .. ] $ toList un
122
+ rn = mapping un
113
123
114
124
-- Checks if two terms are equal up to a given variable renaming. The
115
125
-- renaming should map variables in the right hand term to the
@@ -137,6 +147,93 @@ pattern TAbss vs bd <-
137
147
138
148
{-# COMPLETE TAbss #-}
139
149
150
+ -- Renaming data.
151
+ --
152
+ -- `renames` contains an actual mapping from old variables to new
153
+ -- variables, which is used for the actual substitution.
154
+ --
155
+ -- `conflicts` stores information about which variables should be
156
+ -- avoided. The value at a variable `u` should _at least_ be the
157
+ -- number of variables in `renames` that are being substituted _to_
158
+ -- `u`, because substituting under a binder for `u` will capture those
159
+ -- substitutions. However, `conflicts` can be bootstrapped with extra
160
+ -- counts to avoid ambient variables as well, so that it is possible
161
+ -- to substitute/rewrite expressions without introducing variable
162
+ -- captures.
163
+ data Renaming v = RN
164
+ { conflicts :: Map v Int ,
165
+ renamings :: Map v v
166
+ }
167
+
168
+ -- Creates a Renaming that will avoid the given set of variables.
169
+ avoiding :: Set v -> Renaming v
170
+ avoiding avoid = RN (Map. fromSet (const 1 ) avoid) Map. empty
171
+
172
+ mapping :: (Var v ) => Map v v -> Renaming v
173
+ mapping rn = RN cf rn
174
+ where
175
+ cf = Map. fromListWith (+) . fmap (,1 ) $ Map. elems rn
176
+
177
+ mappingAndAvoiding :: (Var v ) => Map v v -> Set v -> Renaming v
178
+ mappingAndAvoiding rn avoid = RN cf rn
179
+ where
180
+ cf =
181
+ Map. unionWith
182
+ (+)
183
+ (Map. fromSet (const 1 ) avoid)
184
+ (Map. fromListWith (+) . fmap (,1 ) $ Map. elems rn)
185
+
186
+ -- Adjusts a renaming with respect to a remaining set of free
187
+ -- variables. Unnecessary renamings are discarded.
188
+ pruneRenaming :: (Var v ) => Set v -> Renaming v -> Renaming v
189
+ pruneRenaming fvs (RN cf rn) =
190
+ RN
191
+ { renamings = Map. restrictKeys rn fvs,
192
+ conflicts = Map. foldl' decrement cf $ Map. withoutKeys rn fvs
193
+ }
194
+ where
195
+ decrement sv v = Map. update drop v sv
196
+ drop n
197
+ | n <= 1 = Nothing
198
+ | otherwise = Just (n - 1 )
199
+
200
+ renameVar :: (Var v ) => Renaming v -> v -> v
201
+ renameVar (RN _ rn) u = Map. findWithDefault u u rn
202
+
203
+ -- Tests if the renaming is empty in the sense that it will never
204
+ -- cause bound variables to be renamed. This is _not_ just a test of
205
+ -- whether the substitutions are empty, because the conflicts can
206
+ -- cause variables to need freshening even without variable
207
+ -- substitutions.
208
+ isEmptyRenaming :: Renaming v -> Bool
209
+ isEmptyRenaming = null . conflicts
210
+
211
+ -- Freshens a bound variable with regard to a renaming, yielding the
212
+ -- fresh variable and a renaming appropriate for the term within the
213
+ -- binder. The `Set` should be the free variables of the expression
214
+ -- within the binder, for proper freshening.
215
+ freshenBinder :: (Var v ) => Set v -> Renaming v -> v -> (Renaming v , v )
216
+ freshenBinder fvs rn0@ (RN cf rn) u = (rn', u')
217
+ where
218
+ -- if u conflicts with the renaming, freshen it
219
+ u'
220
+ | u `Map.member` cf = freshIn (fvs `Set.union` Map. keysSet cf) u
221
+ | otherwise = u
222
+
223
+ -- if u needs to be renamed, and it actually occurs in the body,
224
+ -- add it to the Renaming.
225
+ rn'
226
+ | u /= u' && u `Set.member` fvs =
227
+ RN
228
+ { conflicts = Map. insertWith (+) u' 1 cf,
229
+ renamings = Map. alter (const $ Just u') u rn
230
+ }
231
+ | otherwise = rn0
232
+
233
+ freshenBinders ::
234
+ (Var v ) => Set v -> Renaming v -> [v ] -> (Renaming v , [v ])
235
+ freshenBinders fvs = mapAccumL (freshenBinder fvs)
236
+
140
237
-- Simultaneous variable renaming and freshening implementation.
141
238
--
142
239
-- subvs0 is a count of the number of conflicts associated with a
@@ -153,48 +250,22 @@ pattern TAbss vs bd <-
153
250
-- rnv0 is the variable renaming map.
154
251
renamesAndFreshen0 ::
155
252
(Var v , Bifunctor f , Bifoldable f ) =>
156
- Map v Int ->
157
- Map v v ->
253
+ Renaming v ->
158
254
Term f v ->
159
255
Term f v
160
- renamesAndFreshen0 subvs0 rnv0 tm = case tm of
256
+ renamesAndFreshen0 rn0 tm = case tm of
161
257
TAbs u body
162
- | not $ Map. null subvs' ->
163
- TAbs u' (renamesAndFreshen0 subvs' rnv' body)
164
- where
165
- rnv' = Map. alter (const $ adjustment) u rnv
166
- bfvs = freeVars body
167
- -- if u is in the set of variables we're substituting in, it
168
- -- needs to be renamed to avoid capturing things.
169
- u'
170
- | u `Map.member` subvs = freshIn (bfvs `Set.union` Map. keysSet subvs) u
171
- | otherwise = u
172
-
173
- -- if u needs to be renamed to avoid capturing subvs
174
- -- and u actually occurs in the body, then add it to
175
- -- the substitutions
176
- (adjustment, subvs')
177
- | u /= u' && u `Set.member` bfvs =
178
- (Just u', Map. insertWith (+) u' 1 subvs)
179
- | otherwise = (Nothing , subvs)
258
+ | (rn, u') <- freshenBinder (freeVars body) rn u,
259
+ u /= u' || not (isEmptyRenaming rn) ->
260
+ TAbs u' (renamesAndFreshen0 rn body)
180
261
TTm body
181
- | not $ Map. null subvs ->
182
- TTm $ bimap lkup (renamesAndFreshen0 subvs rnv ) body
262
+ | not $ isEmptyRenaming rn ->
263
+ TTm $ bimap (renameVar rn) (renamesAndFreshen0 rn ) body
183
264
_ -> tm
184
265
where
185
266
fvs = freeVars tm
186
267
187
- -- throw out irrelevant renamings
188
- rnv = Map. restrictKeys rnv0 fvs
189
-
190
- lkup u = Map. findWithDefault u u rnv
191
-
192
- -- decrement the variable usage counts for the renamings we threw away
193
- subvs = Map. foldl' decrement subvs0 $ Map. withoutKeys rnv0 fvs
194
- decrement sv v = Map. update drop v sv
195
- drop n
196
- | n <= 1 = Nothing
197
- | otherwise = Just (n - 1 )
268
+ rn = pruneRenaming fvs rn0
198
269
199
270
-- Freshens the bound variables in a term to avoid capturing variables
200
271
-- in the set.
@@ -203,9 +274,7 @@ freshen ::
203
274
Set v ->
204
275
Term f v ->
205
276
Term f v
206
- freshen avoid = renamesAndFreshen0 subvs Map. empty
207
- where
208
- subvs = Map. fromSet (const 1 ) avoid
277
+ freshen avoid = renamesAndFreshen0 (avoiding avoid)
209
278
210
279
-- Renames some variables while also avoiding a given set of variables
211
280
-- for any bindings in the term.
@@ -215,30 +284,24 @@ renamesAvoiding ::
215
284
Map v v ->
216
285
Term f v ->
217
286
Term f v
218
- renamesAvoiding avoid rnv = renamesAndFreshen0 subvs rnv
219
- where
220
- suba = Map. fromSet (const 1 ) avoid
221
- subr = Map. fromListWith (+) . fmap (,1 ) $ Map. elems rnv
222
-
223
- subvs = Map. unionWith (+) suba subr
287
+ renamesAvoiding avoid rnv =
288
+ renamesAndFreshen0 (mappingAndAvoiding rnv avoid)
224
289
225
290
-- Simultaneous variable renaming.
226
291
renames ::
227
292
(Var v , Bifunctor f , Bifoldable f ) =>
228
293
Map v v ->
229
294
Term f v ->
230
295
Term f v
231
- renames rnv tm = renamesAndFreshen0 subvs rnv tm
232
- where
233
- subvs = Map. fromListWith (+) . fmap (,1 ) $ Map. elems rnv
296
+ renames rnv tm = renamesAndFreshen0 (mapping rnv) tm
234
297
235
298
rename ::
236
299
(Var v , Ord v , Bifunctor f , Bifoldable f ) =>
237
300
v ->
238
301
v ->
239
302
Term f v ->
240
303
Term f v
241
- rename old new = renamesAndFreshen0 (Map. singleton new 1 ) ( Map. singleton old new)
304
+ rename old new = renamesAndFreshen0 (mapping $ Map. singleton old new)
242
305
243
306
transform ::
244
307
(Var v , Bifunctor g , Bifoldable f , Bifoldable g ) =>
0 commit comments