Author: Tom Sydney Kerckhove <syd@cs-syd.eu>
Committer: Tom Sydney Kerckhove <NorfairKing@users.noreply.github.com>
Give every rule a directory, and every fact its own module Hopinion.Facts was one module holding fourteen types and re-exporting four more, and a rule was one module with its fact in that pile and the reading of the parse tree that produces it in the shared Extract. So nothing said what a rule cost, nothing said which facts a module needed, and the two shared modules were the two that grew with every rule added. A rule is now a directory. Rule.hs is the check, and beside it goes whatever only that rule needs: Fact.hs for a fact nothing else reads, Extract.hs for the reading that produces it. Five of the six rules need neither and are the one module, in a directory of their own so the shape is the same for all of them. HsNoSemigroupOnText is the one with all three, and it is 178 lines that were spread across three shared modules: 30 of rule, 44 of fact, 104 of reading. The last number was invisible where it sat, and it is what M5 has to stop a later expression rule from paying again. What more than one rule reads is still under Hopinion.Facts, one type per module, and no module re-exports another. A consumer names what it uses. This costs import lines rather than saving them, and that is the trade: what each line says is now true. What no rule owns is in Hopinion.Extract.Ghc: a GHC span as a fact's span, the declaration a span falls inside, and taking an expression apart far enough to see what it is. A rule's own extraction asks that rather than importing Hopinion.Extract, which would have made the two import each other. Three dependencies were hiding behind the re-exports. Hopinion.Compiled and the GenValidSpec rule both use the .hie reader, which reached them through Facts re-exporting Hopinion.Hie, so neither file mentioned it. And validity-aeson was a dependency of the library only because Facts imported Data.Validity.Aeson for orphan instances no Validity instance here needs; the import alone satisfied -Wunused-packages, so removing it took the dependency with it. NameFact is in Facts.Occurrence rather than Facts.Name, where a name-shaped thing would seem to belong. It carries a Span and a ScopeKey, and Facts.Place already imports Facts.Name, so that would have closed a cycle. The name it ended up with is the more honest one: it is an occurrence of a name. Two things the split would otherwise have cost are in the plan instead. The reason an enum-shaped fact is stored as its own spelling was one comment over grouped instances and would have become three copies, which is the shape that goes stale in two places and stays right in the third. And the doctrine that a type belongs to whatever gives it meaning was stated only in the header of the module this change deletes, which is the doctrine this change implements. ModuleContext still carries a field per module-level fact, so it imports the fact of any rule that brings one. That is the one edge left pointing from the middle of the tool at a rule, and it is why the rule's directory holds the fact rather than the fact holding the rule. No behaviour change: no type, field, constructor or function was renamed, added or removed.
Total
1m49s
Created Started Finished
| Time to Start | Worker time | Duration | Time to finish | Idle | |
|---|---|---|---|---|---|
| Config | 0s | 1s | 1s | 1s | 0s |
| Eval | 0s | 14s | 14s | 15s | 0s |
| Build | 9s | 0s | 1m39s | 1m49s | 1m34s |
| Suite | 0s | 15s | 1m49s | 1m49s | 1m34s |