Just a few notes while skimming the thread:
Tangentially, I never understood why GHC can’t rewrite functions to
go
form by itself.You can enable it manually with
-fstatic-argument-transformation
(although for some reason it doesn’t seem to work in this case) . If the function is not inlined then it will a have to allocate a closure for thatgo
function. @sgraf is working on improving it though. I believe the latest idea was to only apply this transformation if it makes it possible to inline the function.
Yes, there’s #18962: SAT should only influence the unfolding · Issues · Glasgow Haskell Compiler / GHC · GitLab and the prototype in !4553: WIP: SAT: Attach SAT'd definition as INLINABLE unfolding · Merge requests · Glasgow Haskell Compiler / GHC · GitLab. Sadly, I continually lack the time and priority to fix the (conceptually unrelated) regressions it introduces. If I were to implement GHC on a green field, this would definitely have been the way I’d have implmented SAT in the first place.
Kind of, but I think in most cases the thunks are forced rather quickly and no leak occurs. So you’d get a lot of false positives. Edsko de Vries from Well-Typed has written the
nothunks
library which can give warnings if there are thunks in your code: Being lazy without getting bloated - Well-Typed: The Haskell Consultants .
Edit: I confused nothunks
with the purported noupdate
primitive/Edsko’s dupIO
package. nothunks
seems like an adequate runtime verification procedure, but a static anlysis would far more helpful. I’ll leave the following 2 paras untouched, but bear in mind that they relate to omitting of update frames with noupdate
.
Note that in this case, the closure of the thunk retains the chain of + 1
s. I’d hypothesize that omitting the update frame here would not improve anything because that thunk is never evaluated before memory runs out.
And if it were evaluated multiple times, I’d rather have updated to a I# 9000#
than retain the chain of closures for the next eval… That would be an example where nothunk
/noupdate
would make things worse.
Do you think it is acceptable, that ghc provides no warning (albeit noisy), of this situation occuring?
I don’t think it’s acceptable, but I wouldn’t pin it on GHC, either.
But perhaps a linter like hlint
could implement a pass that warns about these situations, or flags places where a thunk/data structure is retained over a potentially very long function call.
Alas, my interests are as expansive as my time to pursue them (e.g., during my PhD) is finite.
Perhaps someone else would be interested in writing such a static analysis; I think we could get really cool results quite fast. Definitely worth a publication.