Create deterministic names for all identifiers and variables#629
Conversation
| sanitize c | ||
| | c == '.' = '_' | ||
| | c == '\'' = '_' | ||
| | otherwise = c |
There was a problem hiding this comment.
What about names with symbolic (infix) characters? Surely these would need to be sanitized as well? (See the only convert code from uniquePrefixes.)
There was a problem hiding this comment.
I think if you look at the callsites for newUniqueId, it's always called with a string which is is alphanumeric (prefix). Therefore the sanitising is just important for replacing characters in module names.
There was a problem hiding this comment.
Ah, fair enough. And now that I look more closely, the Haddocks for th-desugar's newUniqueName (on which this function is based) do mention that the string has to be alphanumeric. Perhaps it would suffice to just document that here as well.
| -- Each variable encountered during the traversal will be given a fresh non-unique Name | ||
| -- which is the variable OccName followed by an incrementing counter. | ||
| -- For example: x0 y1 z2 x3 etc | ||
| noExactTyVars :: Data a => a -> a |
There was a problem hiding this comment.
If I understand correctly, then if you call noExactTyVars in two different locations (e.g., (noExactTyVars x, noExactTyVars y)), then there is no guarantee that the Names used in one location will be distinct from the names used the other location. Is that correct? If so, it would be worth documenting this potential pitfall.
As an alternative, we could remove noExactTyVars and instead use freshenTyVarsM everywhere. I don't think this should be too hard to do—there's only one noExactTyVars call site remaining, and I suspect that we could replace it with freshenTyVarsM by making the function which calls it monadic.
There was a problem hiding this comment.
Yes. that's right, fresh names are created within each scope and substituted into the remaining parts of the expression.
There was a problem hiding this comment.
The place where noExactTyVars is called takes care to more precisely substitute into the right places
rather than the code in Data.Singletons.TH.Promote.Defun which also uses the traversal to perform the substitution. I feel like all this code is a bit hairy and I don't fully understand what it's doing so I would prefer to not modify it too intensively unless there are some additional tests which demonstrate what's missing. Is that ok with you Ryan?
There was a problem hiding this comment.
That's fine! I am happy to take a closer look at this myself in a follow-up PR.
|
Thanks for your review @RyanGlScott I will return to this tomorrow. I've finished chasing down the other determinism issues for now! |
|
I've made some of the changes you suggest @RyanGlScott. I'm not so familar with this code so I'm quite wary to embark on any major refactorings, perhaps this would be a good interim step and provide a baseline for improving the name freshening story at a later point. |
|
Thanks! (Did you mean to push your changes? I see that various discussion threads have been closed, but the code in the PR itself hasn't yet changed.) On the topic of removing As a result, I am wondering if a better approach would be to port the unique identifier machinery from this PR over to What do you think about this idea? It would unfortunately take some more work to implement, but if our goal is having a high degree of confidence that the output is deterministic, then I think we'll need to do something like this. (If you don't want to do this, that's fine too—I can understand if you feel like this has been more work than you originally anticipated.) |
b2297bf to
bb9521d
Compare
|
@RyanGlScott I pushed now, I accidentally pushed to the wrong remote before. I'm able to fix the level at whichever level is best for the ecosystem. For the project which I tested this on, these changes were sufficient. I am currently trying to modify the test harness to see if it can test for these kinds of failures so that we don't regress in the future if something changes. |
bb9521d to
3b309fc
Compare
|
I've modified the testsuite now to check for deterministic interface files for all the tests. This has highlighted there are still some issues, perhaps that is your observation about the |
3b309fc to
2b593a8
Compare
|
Thanks for being extra thorough here! The determinism-related tests failing matches my expectations, as I suspect that every use of As a particularly simple example, the use of {-# LANGUAGE GHC2024 #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeAbstractions #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Bug where
import Data.Singletons.TH
$(singletons [d|
f :: a -> a
f x = y
where
y = x
|])You'll see different |
|
Thanks Ryan, I added this test and it passes the determinism check on the branch. Here is a minimised version of one of the failures: which results in |
|
Updating I will open a PR over there. |
2b593a8 to
25bb7f4
Compare
|
@RyanGlScott I pushed another update which should be passing CI now (with the updated th-desugar commit). |
RyanGlScott
left a comment
There was a problem hiding this comment.
Fantastic work! At this point, I think my only question concerns which parts of the code should live in th-desugar versus singletons-th.
8c33d31 to
e1c258e
Compare
|
@RyanGlScott I have pushed the changes to |
There are currently three sources of non-determinism when using singletons-th. 1. Let bindings are promoted to a top-level type family named Let<UNIQUE>. 2. The `noExactTyVars` function shifts the unique into the OccName of a name in order to make distinct occnames. 3. Names generated using `newUniqueName` from `th-desugar` contain a unique (see goldfirere/th-desugar#239) Now there are two mechanisms to provide deterministic names in these two cases. 1. The `newUnique` function from `th-desugar` is used to get a unique from an incrementing counter to use for the name of `Let` bindings. 2. For the names which require local uniqueness, `noExactTyVars` carries its own counter and renaming in order to substitute fresh names into a local scope. 3. `th-desugar` is updated to also use an incrementing counter for `newUniqueName`. Fixes goldfirere#628
e1c258e to
d3c4b6e
Compare
RyanGlScott
left a comment
There was a problem hiding this comment.
Thanks again, @mpickering! Let me know if you need new releases of singletons-{th,base} soon with these changes.
@RyanGlScott If you had the time I would appreciate your feedback on the approach in this patch before I invest time finishing things off.