{"slug": "meaning-of-strictness", "title": "Meaning of strictness", "summary": "A Haskell developer explains that strictness analysis, a denotational concept, does not guarantee a function evaluates its argument on every code path, as demonstrated by the function \\x y -> x `seq` y, which is strict in y but never evaluates y if x diverges. This gap between denotational semantics and operational behavior has caused practical issues in GHC that are difficult to fix without regressions.", "body_md": "Indeed, “`f`\n\nis strict” is a neat mathy notion from topology, apparently remote from “computing”, yet it supposedly has actual operational content: any non-constant program `f : Int -> Int`\n\nthat reliably diverges/errors when its argument does *must* evaluate that argument on every code path (at least once); otherwise it solves the halting problem.\n\nThat is the property we exploit in Haskell. Equational reasoning gives us a nice way to reason about a function’s strictness, and hence a way to reason about its operational behavior too! Furthermore, we can justify switching from call-by-need to call-by-value (an operational flavour) by trusting the results of a *strictness* analysis (a denotational flavour). The latter is justified by a denotational semantics, not by an operational one.\n\nIn fact, there’s a subtlety. Strictness analysis is not *actually* a safe “evaluated at least once” analysis. The function `\\x y -> x `seq` y`\n\nis strict in both `x`\n\nand `y`\n\n, but if `x`\n\ndiverges we never evaluate `y`\n\n! So “strictness = evaluated at least once” is wrong if we actually start caring about diverging programs, or just the distinction between crashing and diverging (or crashing differently).\n\nAnother way to put it: strictness isn’t a [ safety property](https://en.wikipedia.org/wiki/Safety_and_liveness_properties). Being strict in\n\n`y`\n\nis supposed to guarantee that `y`\n\ngets evaluated, and a safety property would let us point to the exact step/trace prefix where that happens. But on the diverging `x `seq` y`\n\ntrace there is no such step: we get stuck evaluating `x`\n\n, never reach `y`\n\n, yet `f`\n\nis still strict in `y`\n\n. The denotational semantics is blind to this, because it collapses every divergence to the same `_|_`\n\n. IMO that’s one of the big gaping holes in traditional (non-trace-based) denotational semantics.I think Lennart’s example points at the exact same problem at the intuitive level.\n\nThis subtlety has resurfaced in GHC a number of times, and it’s practically unfixable without incurring regressions in production code.", "url": "https://wpnews.pro/news/meaning-of-strictness", "canonical_source": "https://discourse.haskell.org/t/meaning-of-strictness/14326#post_10", "published_at": "2026-06-29 22:16:38+00:00", "updated_at": "2026-06-29 22:21:59.838016+00:00", "lang": "en", "topics": ["machine-learning", "developer-tools"], "entities": ["Haskell", "GHC", "Lennart"], "alternates": {"html": "https://wpnews.pro/news/meaning-of-strictness", "markdown": "https://wpnews.pro/news/meaning-of-strictness.md", "text": "https://wpnews.pro/news/meaning-of-strictness.txt", "jsonld": "https://wpnews.pro/news/meaning-of-strictness.jsonld"}}