]> Repositorios git - classgraph.git/commitdiff
Document data-fam-instance skip in reduceTypeArg
authorJavier Sagredo <[email protected]>
Thu, 7 May 2026 00:03:36 +0000 (02:03 +0200)
committerJavier Sagredo <[email protected]>
Thu, 7 May 2026 00:03:36 +0000 (02:03 +0200)
Append a bullet to the resolution-determinism section explaining
why reduceTypeArg skips fiIsData entries: the data-family R: rewrite
makes their fiRhs structurally equal to the input FamilyApp, so
naïve recursion looped forever. Also clarify the prior
UndecidableInstances bullet — the reducer DOES recurse, it relies
on structural progress in the RHS to terminate.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
docs/INTERNALS.md

index f772fbf431c03304be8297ed5aeb3290915bdde7..72cd3b6688664a09e76fd15954756e9db9db4867 100644 (file)
@@ -331,13 +331,28 @@ more nuanced. Here's what can and cannot vary:
   `1 + 1` where `(+)` is `GHC.TypeNats.+`, the viewer will stop at
   `+` rather than producing `2`.
 
-- **`UndecidableInstances` / cyclic reduction.** The reducer is
-  iterative, not fixed-point: each call visits the structure once and
-  then bails. We don't loop forever on a divergent chain. In practice
-  this means a circular family like `type instance F a = G a` /
-  `type instance G a = F a` shows up as two separate fam-instance
-  nodes pointing at each other via `fiRhs` references, but no edge in
-  the resolution-chain layer.
+- **`UndecidableInstances` / cyclic reduction.** `reduceTypeArg`
+  recurses on `applySubst(fi.fiRhs, subst)` after a matching
+  fam-instance is found, so a `type instance F a = G a` / `type
+  instance G a = F a` pair *would* loop forever if it weren't for
+  the fact that each step requires structural progress (the RHS
+  isn't structurally equal to the LHS). In practice such a circular
+  pair shows up as two separate fam-instance nodes pointing at each
+  other via `fiRhs` references, with no resolution-chain edge.
+
+- **Data fam-instances are skipped by `reduceTypeArg` entirely.**
+  After the data-family R: rewrite at extraction time (see
+  `Classgraph.Extract`'s `typeArg`), a data fam-instance's `fiRhs`
+  is structurally equal to its LHS — for `data instance Crate Int`,
+  `fiRhs = FamilyApp Crate [Int]`. Reducing `FamilyApp Crate [Int]`
+  through that fam-instance just produces `FamilyApp Crate [Int]`
+  again, so naïvely recursing through it loops forever. The reducer
+  guards against this by skipping any fam-instance with
+  `fiIsData = true`; data-family use sites then fall through
+  unchanged and are surfaced by the predicate-node + chain machinery
+  as the use sites they actually are. The synthetic R: TyCon (the
+  pre-rewrite form) was opaque for the same reason — we just never
+  hit the loop because the rewriter wasn't yet circular.
 
 So: *the data is deterministic and faithful, the chains are an honest
 best-effort visualization*. When in doubt, the chain edges in the