hlint icon indicating copy to clipboard operation
hlint copied to clipboard

Hint `Redundant <&>` suggests code obfuscation

Open andreasabel opened this issue 11 months ago • 3 comments

main = do
  e <- getContents <&> pExp . myLexer >>= \case
    Left err -> do
      putStrLn err
      exitFailure
    Right e -> pure e

This triggers this linter warning:

Warning: Redundant <&>
Found:
  getContents <&> pExp . myLexer
    >>=
      \case
        Left err
          -> do putStrLn err
                exitFailure
        Right e -> pure e
Perhaps:
  getContents
    >>=
      (\case
         Left err
           -> do putStrLn err
                 exitFailure
         Right e -> pure e)
        . pExp . myLexer

Clearly, you do not want such a refactoring. (Imagine the case distinction was even larger.)

In fact the whole point of using <&> in my original expression is that I can write a monadic case in a readable way without introducing a throw-away variable.

There might be similar problems with similar hints.
They hardly take the size of expressions into account, or pipelining styles.

andreasabel avatar Dec 09 '24 14:12 andreasabel

In general, I tend to ignore all hints that suggest stuff like "fuse map into foldr" or any form of "fuse fmap into bind", etc, as they all often have similar issues

googleson78 avatar Dec 09 '24 15:12 googleson78

This may point to a systematic problem of hlint: it suggests mathematically correct refactorings, but code has two consumers, the compiler, and the human reader, and a refactoring must be sound for both of them.

andreasabel avatar Dec 12 '24 07:12 andreasabel

Perhaps a reasonable rule is: if the suggestion requires extra brackets, avoid it. This is however not very trivial to implement, and I'm not sure if it should always be applied.

zliu41 avatar Dec 13 '24 23:12 zliu41