I’ve had a look and I can see a few issues from the point of view of “make invalid laziness unrepresentable”.
The space leak was fixed by force
ing the listen
of a Logger
, so it’s clear the space leak exists in the Logger
’s state.
Now, warnings are reported by tell
ing errorMessage
or errorMessage'
(for example). But tell
does not evaluate what it is told (partly because it is normally told a singleton list), nor does errorMessage
evaluate the message (see the definitions), so the ErrorMessage
s inside MultipleErrors
will typically end up being thunks, holding on to whatever values they reference at their creation site. Even worse, ErrorMessage
itself contains a list, plus a lazy SimpleErrorMessage
, and the fields of SimpleErrorMessage
are all lazy too.
I don’t know which or how many uses of tell
/errorMessage
('
) ends up causing the problem, but here’s an example:
makeResult :: ([[Binder]], (Either RedundancyError Bool, [[Binder]])) -> m Expr
makeResult (bss, (rr, bss')) =
do unless (null bss') tellRedundant
case rr of
Left Incomplete -> tellIncomplete
_ -> return ()
return $ if null bss
then expr
else addPartialConstraint (second null (splitAt 5 bss)) expr
where
tellRedundant = tell . errorMessage' ss . uncurry OverlappingPattern . second null . splitAt 5 $ bss'
tellIncomplete = tell . errorMessage' ss $ IncompleteExhaustivityCheck
Because of the laziness of tell
, errorMessage'
and OverlappingPattern
, tellRedundant
keeps hold of all of bss'
even though it only ends up using the first 5 elements. If bss'
was very big (perhaps itself holding on to other things, due to its own laziness), then that’s a problem! I suspect that this particular case probably isn’t a problem, because bss'
probably isn’t likely to be a long list, but I hope it’s illustrative of the fact that values from a long way away can be held onto for a long time because of laziness. The fact that force
fixes the space leak strongly suggests that one or more of the constructors of SimpleErrorMessage
is keeping values alive longer than necessary in a lazy field.
The fix from the point of view of “make invalid laziness unrepresentable” is to change Logger
to tell
things strictly, and to make ErrorMessage
and SimpleErrorMessage
have strict fields. There’s an additional complication that there are many lazy lists floating around too. It’s harder to know what to do about them. It depends on context.