Deepseq versus "make invalid laziness unrepresentable"

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 forceing the listen of a Logger, so it’s clear the space leak exists in the Logger’s state.

Now, warnings are reported by telling 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 ErrorMessages 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.