<div dir="ltr"><div>Template Haskell with its ability to do arbitrary IO is non-deterministic by</div><div>design. You could for example embed the current date in a file. There is</div><div>however one kind of non-deterministic behavior that you can trigger </div><div>accidentally. It has to do with how Names are reified. If you take a look at </div><div>the definition of reifyName you can see that it puts the assigned Unique in a</div><div>NameU:</div><div><br></div><div>  reifyName :: NamedThing n => n -> TH.Name</div><div>  reifyName thing</div><div>    | isExternalName name = mk_varg pkg_str mod_str occ_str</div><div>    | otherwise           = TH.mkNameU occ_str (getKey (getUnique name))</div><div>    ...</div><div><span class="" style="white-space:pre">    </span></div><div>NameFlavour which NameU is a constructor of has a default Ord instance, meaning</div><div>that it ends up comparing the Uniques. The relative ordering of Uniques is not</div><div>guaranteed to be stable across recompilations [1], so this can lead to </div><div>ABI-incompatible binaries.</div><div><br></div><div>This isn't an abstract problem and it actually happens in practice. The </div><div>microlens package keeps Names in a Set and later turns that set into a list.</div><div>The results have different orders of TyVars resulting in different ABI hashes</div><div>and can potentially be optimized differently.</div><div><br></div><div>I believe it's worth to handle this case in a deterministic way and I have a</div><div>solution in mind. The idea is to extend NameU (and potentially NameL) with an</div><div>ordering key. To be more concrete:</div><div><br></div><div>-   | NameU !Int</div><div>+   | NameU !Int !Int</div><div><br></div><div>This way the Ord instance can use a stable key and the problem reduces to</div><div>ensuring the keys are stable. To generate stable keys we can use the fact that</div><div>reify traverses the expressions in the same order every time and sequentially</div><div>allocate new keys based on traversal order. The way I have it implemented now </div><div>is to add a new field in TcGblEnv which maps Uniques to allocated keys:</div><div><br></div><div>+        tcg_th_names :: TcRef (UniqFM Int, Int),</div><div><br></div><div>Then the reifyName and qNewName do the necessary bookkeeping and translate the</div><div>Uniques on the fly.</div><div><br></div><div>This is a breaking change and it doesn't fix the problem that NameFlavour is</div><div>not abstract and leaks the Uniques. It would break at least:</div><div><br></div><div>- singletons</div><div>- th-lift</div><div>- haskell-src-meta</div><div>- shakespeare</div><div>- distributed-closure</div><div><br></div><div>I'd like to get feedback if this is an acceptable solution and if the problem</div><div>is worth solving.</div><div><br></div><div>Cheers,</div><div>Bartosz</div><div><br></div><div>[1] <a href="https://ghc.haskell.org/trac/ghc/wiki/DeterministicBuilds#NondeterministicUniques">https://ghc.haskell.org/trac/ghc/wiki/DeterministicBuilds#NondeterministicUniques</a></div></div>