packages feed

hlint-3.2.4: data/hlint.yaml

# hlint configuration file
# ==================================

# The hlint tool is mainly automatic, but some hints/restrictions can be specified here.

- package:
    name: base
    modules:
    - import Prelude
    - import Control.Arrow
    - import Control.Exception
    - import Control.Monad
    - import Control.Monad.Trans.State
    - import qualified Data.Foldable
    - import Data.Foldable(asum, sequenceA_, traverse_, for_)
    - import Data.Traversable(traverse, for)
    - import Control.Applicative
    - import Data.Bifunctor
    - import Data.Function
    - import Data.Int
    - import Data.Char
    - import Data.List as Data.List
    - import Data.List as X
    - import Data.Maybe
    - import Data.Monoid
    - import System.IO
    - import Control.Concurrent.Chan
    - import System.Mem.Weak
    - import Control.Exception.Base
    - import System.Exit
    - import Data.Either
    - import Numeric

    - import IO as System.IO
    - import List as Data.List
    - import Maybe as Data.Maybe
    - import Monad as Control.Monad
    - import Char as Data.Char

- package:
    name: lens
    modules:
    - import Control.Lens
    - import Control.Lens.Operators
    - import Control.Monad.Reader

- package:
    name: attoparsec
    modules:
    - import Data.Attoparsec.Text
    - import Data.Attoparsec.ByteString

- package:
    name: codeworld-api
    modules:
    - import CodeWorld

- group:
    name: default
    enabled: true
    imports:
    - package base
    rules:

    # I/O

    - warn: {lhs: putStrLn (show x), rhs: print x}
    - warn: {lhs: mapM_ putChar, rhs: putStr}
    - warn: {lhs: hGetChar stdin, rhs: getChar}
    - warn: {lhs: hGetLine stdin, rhs: getLine}
    - warn: {lhs: hGetContents stdin, rhs: getContents}
    - warn: {lhs: hPutChar stdout, rhs: putChar}
    - warn: {lhs: hPutStr stdout, rhs: putStr}
    - warn: {lhs: hPutStrLn stdout, rhs: putStrLn}
    - warn: {lhs: hPrint stdout, rhs: print}
    - warn: {lhs: hWaitForInput a 0, rhs: hReady a}
    - warn: {lhs: hPutStrLn a (show b), rhs: hPrint a b}
    - warn: {lhs: hIsEOF stdin, rhs: isEOF}
    - warn: {lhs: withFile f WriteMode (\h -> hPutStr h x), rhs: writeFile f x}
    - warn: {lhs: withFile f WriteMode (\h -> hPutStrLn h x), rhs: writeFile f (x ++ "\n")}
    - warn: {lhs: withFile f AppendMode (\h -> hPutStr h x), rhs: appendFile f x}
    - warn: {lhs: withFile f AppendMode (\h -> hPutStrLn h x), rhs: appendFile f (x ++ "\n")}

    # EXIT

    - warn: {lhs: exitWith ExitSuccess, rhs: exitSuccess}

    # ORD

    - warn: {lhs: not (a == b), rhs: a /= b, note: incorrect if either value is NaN}
    - warn: {lhs: not (a /= b), rhs: a == b, note: incorrect if either value is NaN}
    - warn: {lhs: not (a >  b), rhs: a <= b, note: incorrect if either value is NaN}
    - warn: {lhs: not (a >= b), rhs: a <  b, note: incorrect if either value is NaN}
    - warn: {lhs: not (a <  b), rhs: a >= b, note: incorrect if either value is NaN}
    - warn: {lhs: not (a <= b), rhs: a >  b, note: incorrect if either value is NaN}
    - warn: {lhs: compare x y /= GT, rhs: x <= y}
    - warn: {lhs: compare x y == LT, rhs: x < y}
    - warn: {lhs: compare x y /= LT, rhs: x >= y}
    - warn: {lhs: compare x y == GT, rhs: x > y}
    - warn: {lhs: compare x y == EQ, rhs: x == y}
    - warn: {lhs: compare x y /= EQ, rhs: x /= y}
    - warn: {lhs: head (sort x), rhs: minimum x}
    - warn: {lhs: last (sort x), rhs: maximum x}
    - warn: {lhs: head (sortBy f x), rhs: minimumBy f x, side: isCompare f}
    - warn: {lhs: last (sortBy f x), rhs: maximumBy f x, side: isCompare f}
    - warn: {lhs: reverse (sortBy f x), rhs: sortBy (flip f) x, name: Avoid reverse, side: isCompare f, note: Stabilizes sort order}
    - warn: {lhs: sortBy (flip (comparing f)), rhs: sortOn (Down . f)}
    - warn: {lhs: sortBy (comparing f), rhs: sortOn f, side: notEq f fst && notEq f snd}
    - warn: {lhs: reverse (sortOn f x), rhs: sortOn (Data.Ord.Down . f) x, name: Avoid reverse, note: Stabilizes sort order}
    # This suggestion likely costs performance, see https://github.com/ndmitchell/hlint/issues/669#issuecomment-607154496
    # - warn: {lhs: reverse (sort x), rhs: sortOn Data.Ord.Down x, name: Avoid reverse, note: Stabilizes sort order}
    - hint: {lhs: flip (g `on` h), rhs: flip g `on` h, name: Move flip}
    - hint: {lhs: (f `on` g) `on` h, rhs: f `on` (g . h), name: Fuse on/on}


    # READ/SHOW

    - warn: {lhs: showsPrec 0 x "", rhs: show x}
    - warn: {lhs: readsPrec 0, rhs: reads}
    - warn: {lhs: showsPrec 0, rhs: shows}
    - hint: {lhs: showIntAtBase 16 intToDigit, rhs: showHex}
    - hint: {lhs: showIntAtBase 8 intToDigit, rhs: showOct}

    # LIST

    - warn: {lhs: concat (map f x), rhs: concatMap f x}
    - warn: {lhs: concat (fmap f x), rhs: concatMap f x}
    - hint: {lhs: "concat [a, b]", rhs: a ++ b}
    - hint: {lhs: map f (map g x), rhs: map (f . g) x, name: Use map once}
    - hint: {lhs: concatMap f (map g x), rhs: concatMap (f . g) x, name: Fuse concatMap/map}
    - hint: {lhs: x !! 0, rhs: head x}
    - warn: {lhs: take n (repeat x), rhs: replicate n x}
    - warn: {lhs: map f (replicate n x), rhs: replicate n (f x)}
    - warn: {lhs: map f (repeat x), rhs: repeat (f x)}
    - warn: {lhs: "cycle [x]", rhs: repeat x}
    - warn: {lhs: head (reverse x), rhs: last x}
    - warn: {lhs: head (drop n x), rhs: x !! n, side: isNat n}
    - warn: {lhs: head (drop n x), rhs: x !! max 0 n, side: not (isNat n) && not (isNeg n)}
    - warn: {lhs: reverse (tail (reverse x)), rhs: init x, note: IncreasesLaziness}
    - warn: {lhs: reverse (reverse x), rhs: x, note: IncreasesLaziness, name: Avoid reverse}
    - warn: {lhs: isPrefixOf (reverse x) (reverse y), rhs: isSuffixOf x y}
    - warn: {lhs: "foldr (++) []", rhs: concat}
    - warn: {lhs: foldr (++) "", rhs: concat}
    - warn: {lhs: "foldr ((++) . f) []", rhs: concatMap f}
    - warn: {lhs: foldr ((++) . f) "", rhs: concatMap f}
    - warn: {lhs: "foldl (++) []", rhs: concat, note: IncreasesLaziness}
    - warn: {lhs: foldl (++) "", rhs: concat, note: IncreasesLaziness}
    - warn: {lhs: foldl f (head x) (tail x), rhs: foldl1 f x}
    - warn: {lhs: foldr f (last x) (init x), rhs: foldr1 f x}
    - warn: {lhs: "foldr (\\c a -> x : a) []", rhs: "map (\\c -> x)"}
    - warn: {lhs: foldr (.) id l z, rhs: foldr ($) z l}
    - warn: {lhs: span (not . p), rhs: break p}
    - warn: {lhs: break (not . p), rhs: span p}
    - warn: {lhs: "(takeWhile p x, dropWhile p x)", rhs: span p x, note: DecreasesLaziness}
    - warn: {lhs: fst (span p x), rhs: takeWhile p x}
    - warn: {lhs: snd (span p x), rhs: dropWhile p x}
    - warn: {lhs: fst (break p x), rhs: takeWhile (not . p) x}
    - warn: {lhs: snd (break p x), rhs: dropWhile (not . p) x}
    - warn: {lhs: "(take n x, drop n x)", rhs: splitAt n x, note: DecreasesLaziness}
    - warn: {lhs: fst (splitAt p x), rhs: take p x}
    - warn: {lhs: snd (splitAt p x), rhs: drop p x}
    - warn: {lhs: concatMap (++ "\n"), rhs: unlines}
    - warn: {lhs: map id, rhs: id}
    - warn: {lhs: concatMap id, rhs: concat}
    - warn: {lhs: or (map p x), rhs: any p x}
    - warn: {lhs: and (map p x), rhs: all p x}
    - warn: {lhs: any f (map g x), rhs: any (f . g) x}
    - warn: {lhs: all f (map g x), rhs: all (f . g) x}
    - warn: {lhs: "zipWith (,)", rhs: zip}
    - warn: {lhs: "zipWith3 (,,)", rhs: zip3}
    - hint: {lhs: map fst &&& map snd, rhs: unzip}
    - hint: {lhs: length x == 0, rhs: null x, note: IncreasesLaziness}
    - hint: {lhs: 0 == length x, rhs: null x, note: IncreasesLaziness}
    - hint: {lhs: length x < 1, rhs: null x, note: IncreasesLaziness}
    - hint: {lhs: 1 > length x, rhs: null x, note: IncreasesLaziness}
    - hint: {lhs: length x <= 0, rhs: null x, note: IncreasesLaziness}
    - hint: {lhs: 0 >= length x, rhs: null x, note: IncreasesLaziness}
    - hint: {lhs: "x == []", rhs: null x}
    - hint: {lhs: "[] == x", rhs: null x}
    - hint: {lhs: all (const False), rhs: "null"}
    - hint: {lhs: any (const True) x, rhs: not (null x), name: Use null}
    - hint: {lhs: length x /= 0, rhs: not (null x), note: IncreasesLaziness, name: Use null}
    - hint: {lhs: 0 /= length x, rhs: not (null x), note: IncreasesLaziness, name: Use null}
    - hint: {lhs: "\\x -> [x]", rhs: "(:[])", name: "Use :"}
    - hint: {lhs: map f (zip x y), rhs: zipWith (curry f) x y, side: not (isApp f)}
    - hint: {lhs: "map f (fromMaybe [] x)", rhs: "maybe [] (map f) x"}
    - warn: {lhs: not (elem x y), rhs: notElem x y}
    - hint: {lhs: foldr f z (map g x), rhs: foldr (f . g) z x, name: Fuse foldr/map}
    - warn: {lhs: "x ++ concatMap (' ':) y", rhs: "unwords (x:y)"}
    - warn: {lhs: intercalate " ", rhs: unwords}
    - hint: {lhs: concat (intersperse x y), rhs: intercalate x y, side: notEq x " "}
    - hint: {lhs: concat (intersperse " " x), rhs: unwords x}
    - warn: {lhs: null (filter f x), rhs: not (any f x), name: Use any}
    - warn: {lhs: "filter f x == []", rhs: not (any f x), name: Use any}
    - warn: {lhs: "filter f x /= []", rhs: any f x}
    - warn: {lhs: any id, rhs: or}
    - warn: {lhs: all id, rhs: and}
    - warn: {lhs: any (not . f) x, rhs: not (all f x), name: Hoist not}
    - warn: {lhs: all (not . f) x, rhs: not (any f x), name: Hoist not}
    - warn: {lhs: any ((==) a), rhs: elem a, note: ValidInstance Eq a}
    - warn: {lhs: any (== a), rhs: elem a}
    - warn: {lhs: any (a ==), rhs: elem a, note: ValidInstance Eq a}
    - warn: {lhs: all ((/=) a), rhs: notElem a, note: ValidInstance Eq a}
    - warn: {lhs: all (/= a), rhs: notElem a, note: ValidInstance Eq a}
    - warn: {lhs: all (a /=), rhs: notElem a, note: ValidInstance Eq a}
    - warn: {lhs: elem True, rhs: or}
    - warn: {lhs: notElem False, rhs: and}
    - warn: {lhs: True `elem` l, rhs: or l}
    - warn: {lhs: False `notElem` l, rhs: and l}
    - warn: {lhs: findIndex ((==) a), rhs: elemIndex a}
    - warn: {lhs: findIndex (a ==), rhs: elemIndex a}
    - warn: {lhs: findIndex (== a), rhs: elemIndex a}
    - warn: {lhs: findIndices ((==) a), rhs: elemIndices a}
    - warn: {lhs: findIndices (a ==), rhs: elemIndices a}
    - warn: {lhs: findIndices (== a), rhs: elemIndices a}
    - warn: {lhs: "lookup b (zip l [0..])", rhs: elemIndex b l}
    - hint: {lhs: "elem x [y]", rhs: x == y, note: ValidInstance Eq a}
    - hint: {lhs: "notElem x [y]", rhs: x /= y, note: ValidInstance Eq a}
    - hint: {lhs: length x >= 0, rhs: "True", name: Length always non-negative}
    - hint: {lhs: 0 <= length x, rhs: "True", name: Length always non-negative}
    - hint: {lhs: length x > 0, rhs: not (null x), note: IncreasesLaziness, name: Use null}
    - hint: {lhs: 0 < length x, rhs: not (null x), note: IncreasesLaziness, name: Use null}
    - hint: {lhs: length x >= 1, rhs: not (null x), note: IncreasesLaziness, name: Use null}
    - hint: {lhs: 1 <= length x, rhs: not (null x), note: IncreasesLaziness, name: Use null}
    - warn: {lhs: take i x, rhs: "[]", side: isNegZero i, name: Take on a non-positive}
    - warn: {lhs: drop i x, rhs: x, side: isNegZero i, name: Drop on a non-positive}
    - warn: {lhs: last (scanl f z x), rhs: foldl f z x}
    - warn: {lhs: head (scanr f z x), rhs: foldr f z x}
    - warn: {lhs: iterate id, rhs: repeat}
    - warn: {lhs: zipWith f (repeat x), rhs: map (f x)}
    - warn: {lhs: zipWith f y (repeat z), rhs: map (`f` z) y}
    - warn: {lhs: listToMaybe (filter p x), rhs: find p x}
    - warn: {lhs: zip (take n x) (take n y), rhs: take n (zip x y)}
    - warn: {lhs: zip (take n x) (take m y), rhs: take (min n m) (zip x y), side: notEq n m, note: [IncreasesLaziness, DecreasesLaziness], name: Redundant take}

    # MONOIDS

    - warn: {lhs: mempty <> x, rhs: x, name: "Monoid law, left identity"}
    - warn: {lhs: mempty `mappend` x, rhs: x, name: "Monoid law, left identity"}
    - warn: {lhs: x <> mempty, rhs: x, name: "Monoid law, right identity"}
    - warn: {lhs: x `mappend` mempty, rhs: x, name: "Monoid law, right identity"}
    - warn: {lhs: foldr (<>) mempty, rhs: mconcat}
    - warn: {lhs: foldr mappend mempty, rhs: mconcat}
    - warn: {lhs: mempty x, rhs: mempty, name: Evaluate}
    - warn: {lhs: x `mempty` y, rhs: mempty, name: Evaluate, note: "Make sure you didn't mean to use mappend instead of mempty"}

    # TRAVERSABLES

    - warn: {lhs: sequenceA (map f x), rhs: traverse f x}
    - warn: {lhs: sequenceA (fmap f x), rhs: traverse f x}
    - warn: {lhs: sequenceA_ (map f x), rhs: traverse_ f x}
    - warn: {lhs: sequenceA_ (fmap f x), rhs: traverse_ f x}
    - warn: {lhs: foldMap id, rhs: fold}
    - warn: {lhs: fold (fmap f x), rhs: foldMap f x}
    - warn: {lhs: fold (map f x), rhs: foldMap f x}
    - warn: {lhs: foldMap f (fmap g x), rhs: foldMap (f . g) x, name: Fuse foldMap/fmap}
    - warn: {lhs: foldMap f (map g x), rhs: foldMap (f . g) x, name: Fuse foldMap/map}

    # BY

    - warn: {lhs: deleteBy (==), rhs: delete}
    - warn: {lhs: groupBy (==), rhs: group}
    - warn: {lhs: insertBy compare, rhs: insert}
    - warn: {lhs: intersectBy (==), rhs: intersect}
    - warn: {lhs: maximumBy compare, rhs: maximum}
    - warn: {lhs: minimumBy compare, rhs: minimum}
    - warn: {lhs: nubBy (==), rhs: nub}
    - warn: {lhs: sortBy compare, rhs: sort}
    - warn: {lhs: unionBy (==), rhs: union}

    # FOLDS

    - warn: {lhs: foldr  (>>) (return ()), rhs: sequence_}
    - warn: {lhs: foldr  (&&) True, rhs: and}
    - warn: {lhs: foldl  (&&) True, rhs: and, note: IncreasesLaziness}
    - warn: {lhs: foldr1 (&&) , rhs: and, note: "RemovesError on `[]`"}
    - warn: {lhs: foldl1 (&&) , rhs: and, note: "RemovesError on `[]`"}
    - warn: {lhs: foldr  (||) False, rhs: or}
    - warn: {lhs: foldl  (||) False, rhs: or, note: IncreasesLaziness}
    - warn: {lhs: foldr1 (||) , rhs: or, note: "RemovesError on `[]`"}
    - warn: {lhs: foldl1 (||) , rhs: or, note: "RemovesError on `[]`"}
    - warn: {lhs: foldl  (+) 0, rhs: sum}
    - warn: {lhs: foldr  (+) 0, rhs: sum}
    - warn: {lhs: foldl1 (+)  , rhs: sum, note: "RemovesError on `[]`"}
    - warn: {lhs: foldr1 (+)  , rhs: sum, note: "RemovesError on `[]`"}
    - warn: {lhs: foldl  (*) 1, rhs: product}
    - warn: {lhs: foldr  (*) 1, rhs: product}
    - warn: {lhs: foldl1 (*)  , rhs: product, note: "RemovesError on `[]`"}
    - warn: {lhs: foldr1 (*)  , rhs: product, note: "RemovesError on `[]`"}
    - warn: {lhs: foldl1 max  , rhs: maximum}
    - warn: {lhs: foldr1 max  , rhs: maximum}
    - warn: {lhs: foldl1 min  , rhs: minimum}
    - warn: {lhs: foldr1 min  , rhs: minimum}
    - warn: {lhs: foldr mplus mzero, rhs: msum}

    # FUNCTION

    - warn: {lhs: \x -> x, rhs: id}
    - warn: {lhs: \x y -> x, rhs: const}
    - warn: {lhs: curry fst, rhs: const}
    - warn: {lhs: curry snd, rhs: \_ x -> x, note: "Alternatively, use const id"}
    - warn: {lhs: flip const, rhs: \_ x -> x, note: "Alternatively, use const id"}
    - warn: {lhs: "\\(x,y) -> y", rhs: snd}
    - warn: {lhs: "\\(x,y) -> x", rhs: fst}
    - hint: {lhs: "\\x y -> f (x,y)", rhs: curry f}
    - hint: {lhs: "\\(x,y) -> f x y", rhs: uncurry f, note: IncreasesLaziness}
    - warn: {lhs: f (fst p) (snd p), rhs: uncurry f p}
    - warn: {lhs: "uncurry (\\x y -> z)", rhs: "\\(x,y) -> z"}
    - warn: {lhs: "curry (\\(x,y) -> z)", rhs: "\\x y -> z"}
    - warn: {lhs: uncurry (curry f), rhs: f}
    - warn: {lhs: curry (uncurry f), rhs: f}
    - warn: {lhs: ($) (f x), rhs: f x, name: Redundant $}
    - warn: {lhs: (f $), rhs: f, name: Redundant $}
    - warn: {lhs: (Data.Function.& f), rhs: f, name: Redundant Data.Function.&}
    - hint: {lhs: \x -> y, rhs: const y, side: isAtom y && not (isWildcard y)}
        # If any isWildcard recursively then x may be used but not mentioned explicitly
    - warn: {lhs: flip f x y, rhs: f y x, side: isApp original && isAtom y}
    - warn: {lhs: id x, rhs: x}
    - warn: {lhs: id . x, rhs: x, name: Redundant id}
    - warn: {lhs: x . id, rhs: x, name: Redundant id}
    - warn: {lhs: "((,) x)", rhs: "(_noParen_ x,)", name: Use tuple-section, note: RequiresExtension TupleSections}
    - warn: {lhs: "flip (,) x", rhs: "(,_noParen_ x)", name: Use tuple-section, note: RequiresExtension TupleSections}
    - warn: {lhs: flip (flip f), rhs: f, note: DecreasesLaziness}
    - warn: {lhs: flip f <*> g, rhs: f =<< g, name: Redundant flip}
    - warn: {lhs: g <**> flip f, rhs: g >>= f, name: Redundant flip}
    - warn: {lhs: flip f =<< g, rhs: f <*> g, name: Redundant flip}
    - warn: {lhs: g >>= flip f, rhs: g Control.Applicative.<**> f, name: Redundant flip}

    # CHAR

    - warn: {lhs: a >= 'a' && a <= 'z', rhs: isAsciiLower a}
    - warn: {lhs: a >= 'A' && a <= 'Z', rhs: isAsciiUpper a}
    - warn: {lhs: a >= '0' && a <= '9', rhs: isDigit a}
    - warn: {lhs: a >= '0' && a <= '7', rhs: isOctDigit a}
    - warn: {lhs: isLower a || isUpper a, rhs: isAlpha a}
    - warn: {lhs: isUpper a || isLower a, rhs: isAlpha a}

    # BOOL

    - warn: {lhs: x == True, rhs: x, name: Redundant ==}
    - hint: {lhs: x == False, rhs: not x, name: Redundant ==}
    - warn: {lhs: True == a, rhs: a, name: Redundant ==}
    - hint: {lhs: False == a, rhs: not a, name: Redundant ==}
    - warn: {lhs: a /= True, rhs: not a, name: Redundant /=}
    - hint: {lhs: a /= False, rhs: a, name: Redundant /=}
    - warn: {lhs: True /= a, rhs: not a, name: Redundant /=}
    - hint: {lhs: False /= a, rhs: a, name: Redundant /=}
    - warn: {lhs: if a then x else x, rhs: x, note: IncreasesLaziness, name: Redundant if}
    - warn: {lhs: if a then True else False, rhs: a, name: Redundant if}
    - warn: {lhs: if a then False else True, rhs: not a, name: Redundant if}
    - warn: {lhs: if a then t else (if b then t else f), rhs: if a || b then t else f, name: Redundant if}
    - warn: {lhs: if a then (if b then t else f) else f, rhs: if a && b then t else f, name: Redundant if}
    - warn: {lhs: if x then True else y, rhs: x || y, side: notEq y False, name: Redundant if}
    - warn: {lhs: if x then y else False, rhs: x && y, side: notEq y True, name: Redundant if}
    - warn: {lhs: if | b -> t | otherwise -> f, rhs: if b then t else f, name: Redundant multi-way if}
    - hint: {lhs: "case a of {True -> t; False -> f}", rhs: if a then t else f, name: Use if}
    - hint: {lhs: "case a of {False -> f; True -> t}", rhs: if a then t else f, name: Use if}
    - hint: {lhs: "case a of {True -> t; _ -> f}", rhs: if a then t else f, name: Use if}
    - hint: {lhs: "case a of {False -> f; _ -> t}", rhs: if a then t else f, name: Use if}
    - hint: {lhs: "if c then (True, x) else (False, x)", rhs: "(c, x)", note: IncreasesLaziness, name: Redundant if}
    - hint: {lhs: "if c then (False, x) else (True, x)", rhs: "(not c, x)", note: IncreasesLaziness, name: Redundant if}
    - hint: {lhs: "or [x, y]", rhs: x || y}
    - hint: {lhs: "or [x, y, z]", rhs: x || y || z}
    - hint: {lhs: "and [x, y]", rhs: x && y}
    - hint: {lhs: "and [x, y, z]", rhs: x && y && z}
    - warn: {lhs: if x then False else y, rhs: not x && y, side: notEq y True, name: Redundant if}
    - warn: {lhs: if x then y else True, rhs: not x || y, side: notEq y False, name: Redundant if}
    - warn: {lhs: not (not x), rhs: x, name: Redundant not}

    # ARROW

    - warn: {lhs: id *** g, rhs: second g}
    - warn: {lhs: f *** id, rhs: first f}
    - ignore: {lhs: zip (map f x) (map g x), rhs: map (f Control.Arrow.&&& g) x}
    - ignore: {lhs: "\\x -> (f x, g x)", rhs: f Control.Arrow.&&& g}
    - hint: {lhs: "(fst x, snd x)", rhs:  x, note: DecreasesLaziness, name: Redundant pair}

    # BIFUNCTOR

    - warn: {lhs: bimap id g, rhs: second g}
    - warn: {lhs: bimap f id, rhs: first f}
    - warn: {lhs: first id, rhs: id}
    - warn: {lhs: second id, rhs: id}
    - warn: {lhs: bimap id id, rhs: id}
    - warn: {lhs: first f (second g x), rhs: bimap f g x}
    - warn: {lhs: second g (first f x), rhs: bimap f g x}
    - warn: {lhs: first f (first g x), rhs: first (f . g) x}
    - warn: {lhs: second f (second g x), rhs: second (f . g) x}
    - warn: {lhs: bimap f h (bimap g i x), rhs: bimap (f . g) (h . i) x}
    - warn: {lhs: first f (bimap g h x), rhs: bimap (f . g) h x}
    - warn: {lhs: second g (bimap f h x), rhs: bimap f (g . h) x}
    - warn: {lhs: bimap f h (first g x), rhs: bimap (f . g) h x}
    - warn: {lhs: bimap f g (second h x), rhs: bimap f (g . h) x}
    - hint: {lhs: "\\(x,y) -> (f x, g y)", rhs: Data.Bifunctor.bimap f g, note: IncreasesLaziness}
    - hint: {lhs: "\\(x,y) -> (f x,y)", rhs: Data.Bifunctor.first f, note: IncreasesLaziness}
    - hint: {lhs: "\\(x,y) -> (x,f y)", rhs: Data.Bifunctor.second f, note: IncreasesLaziness}
    - hint: {lhs: "(f (fst x), g (snd x))", rhs: Data.Bifunctor.bimap f g x}
    - hint: {lhs: "(f (fst x), snd x)", rhs: Data.Bifunctor.first f x}
    - hint: {lhs: "(fst x, g (snd x))", rhs: Data.Bifunctor.second g x}

    # FUNCTOR

    - warn: {lhs: fmap f (fmap g x), rhs: fmap (f . g) x, name: Functor law}
    - warn: {lhs: f <$> g <$> x, rhs: f . g <$> x, name: Functor law}
    - warn: {lhs: fmap id, rhs: id, name: Functor law}
    - warn: {lhs: id <$> x, rhs: x, name: Functor law}
    - hint: {lhs: fmap f $ x, rhs: f <$> x, side: isApp x || isAtom x}
    - hint: {lhs: \x -> a <$> b x, rhs: fmap a . b}
    - hint: {lhs: x *> pure y, rhs: x Data.Functor.$> y}
    - hint: {lhs: x *> return y, rhs: x Data.Functor.$> y}
    - hint: {lhs: pure x <* y, rhs: x Data.Functor.<$ y}
    - hint: {lhs: return x <* y, rhs: x Data.Functor.<$ y}
    - hint: {lhs: const x <$> y, rhs: x <$ y}
    - hint: {lhs: pure x <$> y, rhs: x <$ y}
    - hint: {lhs: return x <$> y, rhs: x <$ y}
    - hint: {lhs: x <&> const y, rhs: x Data.Functor.$> y}
    - hint: {lhs: x <&> pure y, rhs: x Data.Functor.$> y}
    - hint: {lhs: x <&> return y, rhs: x Data.Functor.$> y}

    # APPLICATIVE

    - hint: {lhs: return x <*> y, rhs: x <$> y}
    - hint: {lhs: pure x <*> y, rhs: x <$> y}
    - warn: {lhs: x <* pure y, rhs: x}
    - warn: {lhs: pure x *> y, rhs: "y"}

    # MONAD

    - warn: {lhs: return a >>= f, rhs: f a, name: "Monad law, left identity"}
    - warn: {lhs: f =<< return a, rhs: f a, name: "Monad law, left identity"}
    - warn: {lhs: m >>= return, rhs: m, name: "Monad law, right identity"}
    - warn: {lhs: return =<< m, rhs: m, name: "Monad law, right identity"}
    - warn: {lhs: liftM, rhs: fmap}
    - warn: {lhs: liftA, rhs: fmap}
    - hint: {lhs: m >>= return . f, rhs: m Data.Functor.<&> f}
    - hint: {lhs: return . f =<< m, rhs: f <$> m}
    - warn: {lhs: fmap f x >>= g, rhs: x >>= g . f}
    - warn: {lhs: f <$> x >>= g, rhs: x >>= g . f}
    - warn: {lhs: x Data.Functor.<&> f >>= g, rhs: x >>= g . f}
    - warn: {lhs: g =<< fmap f x, rhs: g . f =<< x}
    - warn: {lhs: g =<< f <$> x, rhs: g . f =<< x}
    - warn: {lhs: g =<< (x Data.Functor.<&> f), rhs: g . f =<< x}
    - warn: {lhs: if x then y else return (), rhs: Control.Monad.when x $ _noParen_ y, side: not (isAtom y)}
    - warn: {lhs: if x then y else return (), rhs: Control.Monad.when x y, side: isAtom y}
    - warn: {lhs: if x then return () else y, rhs: Control.Monad.unless x $ _noParen_ y, side: isAtom y}
    - warn: {lhs: if x then return () else y, rhs: Control.Monad.unless x y, side: isAtom y}
    - warn: {lhs: sequence (map f x), rhs: mapM f x}
    - warn: {lhs: sequence_ (map f x), rhs: mapM_ f x}
    - warn: {lhs: sequence (fmap f x), rhs: mapM f x}
    - warn: {lhs: sequence_ (fmap f x), rhs: mapM_ f x}
    - hint: {lhs: flip mapM, rhs: Control.Monad.forM}
    - hint: {lhs: flip mapM_, rhs: Control.Monad.forM_}
    - hint: {lhs: flip forM, rhs: mapM}
    - hint: {lhs: flip forM_, rhs: mapM_}
    - warn: {lhs: when (not x), rhs: unless x}
    - warn: {lhs: unless (not x), rhs: when x}
    - warn: {lhs: x >>= id, rhs: Control.Monad.join x}
    - warn: {lhs: id =<< x, rhs: Control.Monad.join x}
    - warn: {lhs: id =<< x, rhs: Control.Monad.join x}
    - warn: {lhs: id =<< x, rhs: Control.Monad.join x}
    - hint: {lhs: join (f <$> x), rhs: f =<< x}
    - hint: {lhs: join (fmap f x), rhs: f =<< x}
    - hint: {lhs: a >> return (), rhs: Control.Monad.void a, side: isAtom a || isApp a}
    - warn: {lhs: fmap (const ()), rhs: Control.Monad.void}
    - warn: {lhs: const () <$> x, rhs: Control.Monad.void x}
    - warn: {lhs: flip (>=>), rhs: (<=<)}
    - warn: {lhs: flip (<=<), rhs: (>=>)}
    - warn: {lhs: flip (>>=), rhs: (=<<)}
    - warn: {lhs: flip (=<<), rhs: (>>=)}
    - hint: {lhs: \x -> f x >>= g, rhs: f Control.Monad.>=> g}
    - hint: {lhs: \x -> f =<< g x, rhs: f Control.Monad.<=< g}
    - hint: {lhs: (>>= f) . g, rhs: f Control.Monad.<=< g}
    - hint: {lhs: (f =<<) . g, rhs: f Control.Monad.<=< g}
    - warn: {lhs: a >> forever a, rhs: forever a}
    - hint: {lhs: liftM2 id, rhs: ap}
    - warn: {lhs: liftA2 f (return x), rhs: fmap (f x)}
    - warn: {lhs: liftM2 f (pure x), rhs: fmap (f x)}
    - warn: {lhs: liftM2 f (return x), rhs: fmap (f x)}
    - warn: {lhs: fmap f (return x), rhs: return (f x)}
    - warn: {lhs: f <$> return x, rhs: return (f x)}
    - warn: {lhs: mapM (uncurry f) (zip l m), rhs: zipWithM f l m}
    - warn: {lhs: mapM_ (void . f), rhs: mapM_ f}
    - warn: {lhs: forM_ x (void . f), rhs: forM_ x f}
    - warn: {lhs: a >>= \_ -> b, rhs: a >> b}
    - warn: {lhs: m <* return x, rhs: m}
    - warn: {lhs: return x *> m, rhs: m}
    - warn: {lhs: pure x >> m, rhs: m}
    - warn: {lhs: return x >> m, rhs: m}

    # STATE MONAD

    - warn: {lhs: fst (runState x y), rhs: evalState x y}
    - warn: {lhs: snd (runState x y), rhs: execState x y}

    # MONAD LIST

    - warn: {lhs: fmap unzip (mapM f x), rhs: Control.Monad.mapAndUnzipM f x}
    - warn: {lhs: sequence (zipWith f x y), rhs: Control.Monad.zipWithM f x y}
    - warn: {lhs: sequence_ (zipWith f x y), rhs: Control.Monad.zipWithM_ f x y}
    - warn: {lhs: sequence (replicate n x), rhs: Control.Monad.replicateM n x}
    - warn: {lhs: sequence_ (replicate n x), rhs: Control.Monad.replicateM_ n x}
    - warn: {lhs: sequenceA (zipWith f x y), rhs: Control.Monad.zipWithM f x y}
    - warn: {lhs: sequenceA_ (zipWith f x y), rhs: Control.Monad.zipWithM_ f x y}
    - warn: {lhs: sequenceA (replicate n x), rhs: Control.Monad.replicateM n x}
    - warn: {lhs: sequenceA_ (replicate n x), rhs: Control.Monad.replicateM_ n x}
    - warn: {lhs: mapM f (replicate n x), rhs: Control.Monad.replicateM n (f x)}
    - warn: {lhs: mapM_ f (replicate n x), rhs: Control.Monad.replicateM_ n (f x)}
    - warn: {lhs: mapM f (map g x), rhs: mapM (f . g) x, name: Fuse mapM/map}
    - warn: {lhs: mapM_ f (map g x), rhs: mapM_ (f . g) x, name: Fuse mapM_/map}
    - warn: {lhs: traverse f (map g x), rhs: traverse (f . g) x, name: Fuse traverse/map}
    - warn: {lhs: traverse_ f (map g x), rhs: traverse_ (f . g) x, name: Fuse traverse_/map}
    - warn: {lhs: mapM id, rhs: sequence}
    - warn: {lhs: mapM_ id, rhs: sequence_}

    # APPLICATIVE / TRAVERSABLE

    - warn: {lhs: flip traverse, rhs: for}
    - warn: {lhs: flip for, rhs: traverse}
    - warn: {lhs: flip traverse_, rhs: for_}
    - warn: {lhs: flip for_, rhs: traverse_}
    - warn: {lhs: foldr (*>) (pure ()), rhs: sequenceA_}
    - warn: {lhs: foldr (<|>) empty, rhs: asum}
    - warn: {lhs: liftA2 (flip ($)), rhs: (<**>)}
    - warn: {lhs: liftA2 f (pure x), rhs: fmap (f x)}
    - warn: {lhs: fmap f (pure x), rhs: pure (f x)}
    - warn: {lhs: f <$> pure x, rhs: pure (f x)}
    - warn: {lhs: Just <$> a <|> pure Nothing, rhs: optional a}
    - hint: {lhs: m >>= pure . f, rhs: m Data.Functor.<&> f}
    - hint: {lhs: pure . f =<< m, rhs: f <$> m}
    - warn: {lhs: empty <|> x, rhs: x, name: "Alternative law, left identity"}
    - warn: {lhs: x <|> empty, rhs: x, name: "Alternative law, right identity"}
    - warn: {lhs: traverse id, rhs: sequenceA}
    - warn: {lhs: traverse_ id, rhs: sequenceA_}


    # LIST COMP

    - hint: {lhs: "if b then [x] else []", rhs: "[x | b]", name: Use list comprehension}
    - hint: {lhs: "if b then [] else [x]", rhs: "[x | not b]", name: Use list comprehension}
    - hint: {lhs: "[x | x <- y]", rhs: "y", side: isVar x, name: Redundant list comprehension}

    # SEQ

    - warn: {lhs: seq x x, rhs: x, name: Redundant seq}
    - warn: {lhs: join seq, rhs: id, name: Redundant seq}
    - warn: {lhs: id $! x, rhs: x, name: Redundant $!}
    - warn: {lhs: seq x y, rhs: "y", side: isWHNF x, name: Redundant seq}
    - warn: {lhs: f $! x, rhs: f x, side: isWHNF x, name: Redundant $!}
    - warn: {lhs: evaluate x, rhs: return x, side: isWHNF x, name: Redundant evaluate}
    - warn: {lhs: seq (rnf x) (), rhs: rnf x, name: Redundant seq}

    # TUPLE

    - warn: {lhs: fst (unzip x), rhs: map fst x}
    - warn: {lhs: snd (unzip x), rhs: map snd x}
    - hint: {lhs: "\\x y -> (x, y)", rhs: "(,)"}
    - hint: {lhs: "\\x y z -> (x, y, z)", rhs: "(,,)"}

    # MAYBE

    - warn: {lhs: maybe x id, rhs: Data.Maybe.fromMaybe x}
    - warn: {lhs: maybe Nothing Just, rhs: id, name: Redundant maybe}
    - warn: {lhs: maybe False (const True), rhs: Data.Maybe.isJust}
    - warn: {lhs: maybe True (const False), rhs: Data.Maybe.isNothing}
    - warn: {lhs: maybe False (x ==), rhs: (Just x ==)}
    - warn: {lhs: maybe True (x /=), rhs: (Just x /=)}
    - warn: {lhs: maybe False (== x), rhs: (Just x ==), note: ValidInstance Eq x}
    - warn: {lhs: maybe True (/= x), rhs: (Just x /=), note: ValidInstance Eq x}
    # The following two hints seem to be somewhat unwelcome, e.g.
    # https://github.com/ndmitchell/hlint/issues/1177
    - ignore: {lhs: fromMaybe False x, rhs: Just True == x} # Eta expanded, see https://github.com/ndmitchell/hlint/issues/970#issuecomment-643645053
    - ignore: {lhs: fromMaybe True x, rhs: Just False /= x}
    - warn: {lhs: not (isNothing x), rhs: isJust x}
    - warn: {lhs: not (isJust x), rhs: isNothing x}
    - warn: {lhs: "maybe [] (:[])", rhs: maybeToList}
    - warn: {lhs: catMaybes (map f x), rhs: mapMaybe f x}
    - warn: {lhs: catMaybes (fmap f x), rhs: mapMaybe f x}
    - hint: {lhs: case x of Nothing -> y; Just a -> a , rhs: Data.Maybe.fromMaybe y x, side: isAtom y, name: Replace case with fromMaybe}
    - hint: {lhs: case x of Just a -> a; Nothing -> y, rhs: Data.Maybe.fromMaybe y x, side: isAtom y, name: Replace case with fromMaybe}
    - hint: {lhs: case x of Nothing -> y; Just a -> f a , rhs: maybe y f x, side: isAtom y && isAtom f, name: Replace case with maybe}
    - hint: {lhs: case x of Just a -> f a; Nothing -> y, rhs: maybe y f x, side: isAtom y && isAtom f, name: Replace case with maybe}
    - warn: {lhs: if isNothing x then y else f (fromJust x), rhs: maybe y f x}
    - warn: {lhs: if isJust x then f (fromJust x) else y, rhs: maybe y f x}
    - warn: {lhs: maybe Nothing (Just . f), rhs: fmap f}
    - hint: {lhs: map fromJust (filter isJust x), rhs:  Data.Maybe.catMaybes x}
    - warn: {lhs: x == Nothing , rhs:  isNothing x}
    - warn: {lhs: Nothing == x , rhs:  isNothing x}
    - warn: {lhs: x /= Nothing , rhs:  Data.Maybe.isJust x}
    - warn: {lhs: Nothing /= x , rhs:  Data.Maybe.isJust x}
    - warn: {lhs: concatMap (maybeToList . f), rhs: Data.Maybe.mapMaybe f}
    - warn: {lhs: concatMap maybeToList, rhs: catMaybes}
    - warn: {lhs: maybe n Just x, rhs: x Control.Applicative.<|> n}
    - warn: {lhs: if isNothing x then y else fromJust x, rhs: fromMaybe y x}
    - warn: {lhs: if isJust x then fromJust x else y, rhs: fromMaybe y x}
    - warn: {lhs: isJust x && (fromJust x == y), rhs: x == Just y}
    - warn: {lhs: mapMaybe f (map g x), rhs: mapMaybe (f . g) x, name: Fuse mapMaybe/map}
    - warn: {lhs: fromMaybe a (fmap f x), rhs: maybe a f x}
    - warn: {lhs: fromMaybe a (f <$> x), rhs: maybe a f x}
    - warn: {lhs: mapMaybe id, rhs: catMaybes}
    - hint: {lhs: "[x | Just x <- a]", rhs: Data.Maybe.catMaybes a}
    - hint: {lhs: case m of Nothing -> Nothing; Just x -> x, rhs: Control.Monad.join m}
    - hint: {lhs: maybe Nothing id, rhs: join}
    - hint: {lhs: maybe Nothing f x, rhs: f =<< x}
    - warn: {lhs: maybe x f (fmap g y), rhs: maybe x (f . g) y, name: Redundant fmap}
    - warn: {lhs: isJust (fmap f x), rhs: isJust x}
    - warn: {lhs: isNothing (fmap f x), rhs: isNothing x}
    - warn: {lhs: fromJust (fmap f x), rhs: f (fromJust x), note: IncreasesLaziness}
    - warn: {lhs: mapMaybe f (fmap g x), rhs: mapMaybe (f . g) x, name: Redundant fmap}

    # EITHER

    - warn: {lhs: "[a | Left a <- a]", rhs: lefts a}
    - warn: {lhs: "[a | Right a <- a]", rhs: rights a}
    - warn: {lhs: either Left (Right . f), rhs: fmap f}
    - warn: {lhs: either f g (fmap h x), rhs: either f (g . h) x, name: Redundant fmap}
    - warn: {lhs: isLeft (fmap f x), rhs: isLeft x}
    - warn: {lhs: isRight (fmap f x), rhs: isRight x}
    - warn: {lhs: fromLeft x (fmap f y), rhs: fromLeft x y}
    - warn: {lhs: fromRight x (fmap f y), rhs: either (const x) f y}
    - warn: {lhs: either (const x) id, rhs: fromRight x}
    - warn: {lhs: either id (const x), rhs: fromLeft x}

    # INFIX

    - hint: {lhs: elem x y, rhs: x `elem` y, side: not (isInfixApp original) && not (isParen result), name: Use infix}
    - hint: {lhs: notElem x y, rhs: x `notElem` y, side: not (isInfixApp original) && not (isParen result), name: Use infix}
    - hint: {lhs: isInfixOf x y, rhs: x `isInfixOf` y, side: not (isInfixApp original) && not (isParen result), name: Use infix}
    - hint: {lhs: isSuffixOf x y, rhs: x `isSuffixOf` y, side: not (isInfixApp original) && not (isParen result), name: Use infix}
    - hint: {lhs: isPrefixOf x y, rhs: x `isPrefixOf` y, side: not (isInfixApp original) && not (isParen result), name: Use infix}
    - hint: {lhs: union x y, rhs: x `union` y, side: not (isInfixApp original) && not (isParen result), name: Use infix}
    - hint: {lhs: intersect x y, rhs: x `intersect` y, side: not (isInfixApp original) && not (isParen result), name: Use infix}

    # MATHS

    - warn: {lhs: fromIntegral x, rhs: x, side: isLitInt x, name: Redundant fromIntegral}
    - warn: {lhs: fromInteger x, rhs: x, side: isLitInt x, name: Redundant fromInteger}
    - hint: {lhs: x + negate y, rhs: x - y}
    - hint: {lhs: 0 - x, rhs: negate x}
    - warn: {lhs: negate (negate x), rhs: x, name: Redundant negate}
    - hint: {lhs: log y / log x, rhs: logBase x y}
    - hint: {lhs: sin x / cos x, rhs: tan x}
    - hint: {lhs: rem n 2 == 0, rhs: even n}
    - hint: {lhs: 0 == rem n 2, rhs: even n}
    - hint: {lhs: rem n 2 /= 0, rhs: odd n}
    - hint: {lhs: 0 /= rem n 2, rhs: odd n}
    - hint: {lhs: mod n 2 == 0, rhs: even n}
    - hint: {lhs: 0 == mod n 2, rhs: even n}
    - hint: {lhs: mod n 2 /= 0, rhs: odd n}
    - hint: {lhs: 0 /= mod n 2, rhs: odd n}
    - hint: {lhs: not (even x), rhs: odd x}
    - hint: {lhs: not (odd x), rhs: even x}
    - hint: {lhs: x ** 0.5, rhs: sqrt x}
    - hint: {lhs: x ^ 0, rhs: "1", name: Use 1}
    - hint: {lhs: round (x - 0.5), rhs: floor x}

    # CONCURRENT

    - hint: {lhs: mapM_ (writeChan a), rhs: writeList2Chan a}
    - error: {lhs: atomically (readTVar x), rhs: readTVarIO x}
    - error: {lhs: atomically (newTVar x), rhs: newTVarIO x}
    - error: {lhs: atomically (newTMVar x), rhs: newTMVarIO x}
    - error: {lhs: atomically newEmptyTMVar, rhs: newEmptyTMVarIO}

    # TYPEABLE

    - hint: {lhs: "typeOf (a :: b)", rhs: "typeRep (Proxy :: Proxy b)"}

    # EXCEPTION

    - hint: {lhs: flip Control.Exception.catch, rhs: handle}
    - hint: {lhs: flip handle, rhs: Control.Exception.catch}
    - hint: {lhs: flip (catchJust p), rhs: handleJust p}
    - hint: {lhs: flip (handleJust p), rhs: catchJust p}
    - hint: {lhs: Control.Exception.bracket b (const a) (const t), rhs: Control.Exception.bracket_ b a t}
    - hint: {lhs: Control.Exception.bracket (openFile x y) hClose, rhs: withFile x y}
    - hint: {lhs: Control.Exception.bracket (openBinaryFile x y) hClose, rhs: withBinaryFile x y}
    - hint: {lhs: throw (ErrorCall a), rhs: error a}
    - warn: {lhs: toException NonTermination, rhs: nonTermination}
    - warn: {lhs: toException NestedAtomically, rhs: nestedAtomically}

    # IOREF

    - hint: {lhs: modifyIORef r (const x), rhs: writeIORef r x}
    - hint: {lhs: modifyIORef r (\v -> x), rhs: writeIORef r x}

    # STOREABLE/PTR

    - hint: {lhs: castPtr nullPtr, rhs: nullPtr}
    - hint: {lhs: castPtr (castPtr x), rhs: castPtr x}
    - hint: {lhs: plusPtr (castPtr x), rhs: plusPtr x}
    - hint: {lhs: minusPtr (castPtr x), rhs: minusPtr x}
    - hint: {lhs: minusPtr x (castPtr y), rhs: minusPtr x y}
    - hint: {lhs: peekByteOff (castPtr x), rhs: peekByteOff x}
    - hint: {lhs: pokeByteOff (castPtr x), rhs: pokeByteOff x}

    # WEAK POINTERS

    - warn: {lhs: mkWeak a a b, rhs: mkWeakPtr a b}
    - warn: {lhs: "mkWeak a (a, b) c", rhs: mkWeakPair a b c}

    # FOLDABLE

    - warn: {lhs: case m of Nothing -> return (); Just x -> f x, rhs: Data.Foldable.forM_ m f}
    - warn: {lhs: case m of Just x -> f x; Nothing -> return (), rhs: Data.Foldable.forM_ m f}
    - warn: {lhs: case m of Just x -> f x; _ -> return (), rhs: Data.Foldable.forM_ m f}
    - warn: {lhs: when (isJust m) (f (fromJust m)), rhs: Data.Foldable.forM_ m f}

    # STATE MONAD

    - warn: {lhs: f <$> Control.Monad.State.get, rhs: gets f}
    - warn: {lhs: fmap f  Control.Monad.State.get, rhs: gets f}
    - warn: {lhs: f <$> Control.Monad.State.gets g, rhs: gets (f . g)}
    - warn: {lhs: fmap f (Control.Monad.State.gets g), rhs: gets (f . g)}
    - warn: {lhs: f <$> Control.Monad.Reader.ask, rhs: asks f}
    - warn: {lhs: fmap f Control.Monad.Reader.ask, rhs: asks f}
    - warn: {lhs: f <$> Control.Monad.Reader.asks g, rhs: asks (f . g)}
    - warn: {lhs: fmap f (Control.Monad.Reader.asks g), rhs: asks (f . g)}
    - warn: {lhs: fst (runState m s), rhs: evalState m s}
    - warn: {lhs: snd (runState m s), rhs: execState m s}

    # EVALUATE

    - warn: {lhs: True && x, rhs: x, name: Evaluate}
    - warn: {lhs: False && x, rhs: "False", name: Evaluate}
    - warn: {lhs: True || x, rhs: "True", name: Evaluate}
    - warn: {lhs: False || x, rhs: x, name: Evaluate}
    - warn: {lhs: not True, rhs: "False", name: Evaluate}
    - warn: {lhs: not False, rhs: "True", name: Evaluate}
    - warn: {lhs: Nothing >>= k, rhs: Nothing, name: Evaluate}
    - warn: {lhs: k =<< Nothing, rhs: Nothing, name: Evaluate}
    - warn: {lhs: either f g (Left x), rhs: f x, name: Evaluate}
    - warn: {lhs: either f g (Right y), rhs: g y, name: Evaluate}
    - warn: {lhs: "fst (x,y)", rhs: x, name: Evaluate}
    - warn: {lhs: "snd (x,y)", rhs: "y", name: Evaluate}
    - warn: {lhs: "init [x]", rhs: "[]", name: Evaluate}
    - warn: {lhs: "null []", rhs: "True", name: Evaluate}
    - warn: {lhs: "length []", rhs: "0", name: Evaluate}
    - warn: {lhs: "foldl f z []", rhs: z, name: Evaluate}
    - warn: {lhs: "foldr f z []", rhs: z, name: Evaluate}
    - warn: {lhs: "foldr1 f [x]", rhs: x, name: Evaluate}
    - warn: {lhs: "scanr f z []", rhs: "[z]", name: Evaluate}
    - warn: {lhs: "scanr1 f []", rhs: "[]", name: Evaluate}
    - warn: {lhs: "scanr1 f [x]", rhs: "[x]", name: Evaluate}
    - warn: {lhs: "take n []", rhs: "[]", note: IncreasesLaziness, name: Evaluate}
    - warn: {lhs: "drop n []", rhs: "[]", note: IncreasesLaziness, name: Evaluate}
    - warn: {lhs: "takeWhile p []", rhs: "[]", name: Evaluate}
    - warn: {lhs: "dropWhile p []", rhs: "[]", name: Evaluate}
    - warn: {lhs: "span p []", rhs: "([],[])", name: Evaluate}
    - warn: {lhs: lines "", rhs: "[]", name: Evaluate}
    - warn: {lhs: "unwords []", rhs: "\"\"", name: Evaluate}
    - warn: {lhs: x - 0, rhs: x, name: Evaluate}
    - warn: {lhs: x * 1, rhs: x, name: Evaluate}
    - warn: {lhs: x / 1, rhs: x, name: Evaluate}
    - warn: {lhs: "concat [a]", rhs: a, name: Evaluate}
    - warn: {lhs: "concat []", rhs: "[]", name: Evaluate}
    - warn: {lhs: "zip [] []", rhs: "[]", name: Evaluate}
    - warn: {lhs: const x y, rhs: x, name: Evaluate}
    - warn: {lhs: any (const False), rhs: const False, note: IncreasesLaziness, name: Evaluate}
    - warn: {lhs: all (const True), rhs: const True, note: IncreasesLaziness, name: Evaluate}
    - warn: {lhs: "[] ++ x", rhs: x, name: Evaluate}
    - warn: {lhs: "x ++ []", rhs: x, name: Evaluate}

    # FOLDABLE + TUPLES

    - warn: {lhs: "foldr   f z (x,b)", rhs: f b z, name: Using foldr on tuple}
    - warn: {lhs: "foldr'  f z (x,b)", rhs: f b z, name: Using foldr' on tuple}
    - warn: {lhs: "foldl   f z (x,b)", rhs: f z b, name: Using foldl on tuple}
    - warn: {lhs: "foldl'  f z (x,b)", rhs: f z b, name: Using foldl' on tuple}
    - warn: {lhs: "foldMap f   (x,b)", rhs: f b, name: Using foldMap on tuple}
    - warn: {lhs: "foldr1  f   (x,b)", rhs: b, name: Using foldr1 on tuple}
    - warn: {lhs: "foldl1  f   (x,b)", rhs: b, name: Using foldl1 on tuple}
    - warn: {lhs: "elem    e   (x,b)", rhs: e == b, name: Using elem on tuple}
    - warn: {lhs: "fold        (x,b)", rhs: b, name: Using fold on tuple}
    - warn: {lhs: "toList      (x,b)", rhs: b, name: Using toList on tuple}
    - warn: {lhs: "maximum     (x,b)", rhs: b, name: Using maximum on tuple}
    - warn: {lhs: "minimum     (x,b)", rhs: b, name: Using minimum on tuple}
    - warn: {lhs: "sum         (x,b)", rhs: b, name: Using sum on tuple}
    - warn: {lhs: "product     (x,b)", rhs: b, name: Using product on tuple}
    - warn: {lhs: "concat      (x,b)", rhs: b, name: Using concat on tuple}
    - warn: {lhs: "and         (x,b)", rhs: b, name: Using and on tuple}
    - warn: {lhs: "or          (x,b)", rhs: b, name: Using or on tuple}
    - warn: {lhs: "any     f   (x,b)", rhs: f b, name: Using any on tuple}
    - warn: {lhs: "all     f   (x,b)", rhs: f b, name: Using all on tuple}

    - warn: {lhs: "foldr   f z (x,y,b)", rhs: f b z, name: Using foldr on tuple}
    - warn: {lhs: "foldr'  f z (x,y,b)", rhs: f b z, name: Using foldr' on tuple}
    - warn: {lhs: "foldl   f z (x,y,b)", rhs: f z b, name: Using foldl on tuple}
    - warn: {lhs: "foldl'  f z (x,y,b)", rhs: f z b, name: Using foldl' on tuple}
    - warn: {lhs: "foldMap f   (x,y,b)", rhs: f b, name: Using foldMap on tuple}
    - warn: {lhs: "foldr1  f   (x,y,b)", rhs: b, name: Using foldr1 on tuple}
    - warn: {lhs: "foldl1  f   (x,y,b)", rhs: b, name: Using foldl1 on tuple}
    - warn: {lhs: "elem    e   (x,y,b)", rhs: e == b, name: Using elem on tuple}
    - warn: {lhs: "fold        (x,y,b)", rhs: b, name: Using fold on tuple}
    - warn: {lhs: "toList      (x,y,b)", rhs: b, name: Using toList on tuple}
    - warn: {lhs: "maximum     (x,y,b)", rhs: b, name: Using maximum on tuple}
    - warn: {lhs: "minimum     (x,y,b)", rhs: b, name: Using minimum on tuple}
    - warn: {lhs: "sum         (x,y,b)", rhs: b, name: Using sum on tuple}
    - warn: {lhs: "product     (x,y,b)", rhs: b, name: Using product on tuple}
    - warn: {lhs: "concat      (x,y,b)", rhs: b, name: Using concat on tuple}
    - warn: {lhs: "and         (x,y,b)", rhs: b, name: Using and on tuple}
    - warn: {lhs: "or          (x,y,b)", rhs: b, name: Using or on tuple}
    - warn: {lhs: "any     f   (x,y,b)", rhs: f b, name: Using any on tuple}
    - warn: {lhs: "all     f   (x,y,b)", rhs: f b, name: Using all on tuple}

    - warn: {lhs: null x  , rhs: "False", side: isTuple x, name: Using null on tuple}
    - warn: {lhs: length x, rhs: "1"    , side: isTuple x, name: Using length on tuple}

    # MAP

    - warn: {lhs: "Data.Map.fromList []", rhs: Data.Map.empty}
    - warn: {lhs: "Data.Map.Lazy.fromList []", rhs: Data.Map.Lazy.empty}
    - warn: {lhs: "Data.Map.Strict.fromList []", rhs: Data.Map.Strict.empty}

- group:
    name: lens
    enabled: true
    imports:
    - package base
    - package lens
    rules:
    - warn: {lhs: "(a ^. b) ^. c", rhs: "a ^. (b . c)"}
    - warn: {lhs: "fromJust (a ^? b)", rhs: "a ^?! b"}
    - warn: {lhs: "a .~ Just b", rhs: "a ?~ b"}
    - warn: {lhs: "a & (mapped %~ b)", rhs: "a <&> b"}
    - warn: {lhs: "a & ((mapped . b) %~ c)", rhs: "a <&> b %~ c"}
    - warn: {lhs: "a & (mapped .~ b)", rhs: "b <$ a"}
    - warn: {lhs: "ask <&> (^. a)", rhs: "view a"}
    - warn: {lhs: "view a <&> (^. b)", rhs: "view (a . b)"}

    # `at` pitfalls:

    - warn: {lhs: "Control.Lens.at a . Control.Lens._Just", rhs: "Control.Lens.ix a"}
    - error: {lhs: "Control.Lens.has (Control.Lens.at a)", rhs: "True"}
    - error: {lhs: "Control.Lens.has (a . Control.Lens.at b)", rhs: "Control.Lens.has a"}
    - error: {lhs: "Control.Lens.nullOf (Control.Lens.at a)", rhs: "False"}
    - error: {lhs: "Control.Lens.nullOf (a . Control.Lens.at b)", rhs: "Control.Lens.nullOf a"}

- group:
    name: use-lens
    enabled: false
    imports:
    - package base
    - package lens
    rules:
    - warn: {lhs: "either Just (const Nothing)", rhs: preview _Left}
    - warn: {lhs: "either (const Nothing) Just", rhs: preview _Right}

- group:
    name: attoparsec
    enabled: true
    imports:
    - package base
    - package attoparsec
    rules:
    - warn: {lhs: Data.Attoparsec.Text.option Nothing (Just <$> p),       rhs: optional p}
    - warn: {lhs: Data.Attoparsec.ByteString.option Nothing (Just <$> p), rhs: optional p}

- group:
    name: generalise
    enabled: false
    imports:
    - package base
    rules:
    - warn: {lhs: map, rhs: fmap}
    - warn: {lhs: a ++ b, rhs: a <> b}
    - warn: {lhs: "sequence [a]", rhs: "pure <$> a"}
    - warn: {lhs: "x /= []", rhs: not (null x), name: Use null}
    - warn: {lhs: "[] /= x", rhs: not (null x), name: Use null}
    - warn: {lhs: "maybe []", rhs: foldMap}

- group:
    name: generalise-for-conciseness
    enabled: false
    imports:
    - package base
    rules:
    - warn: {lhs: maybe mempty, rhs: foldMap}
    - warn: {lhs: maybe False, rhs: any}
    - warn: {lhs: maybe True, rhs: all}
    - warn: {lhs: either (const mempty), rhs: foldMap}
    - warn: {lhs: either mempty, rhs: foldMap}
    - warn: {lhs: either (const False), rhs: any}
    - warn: {lhs: either (const True), rhs: all}
    - warn: {lhs: Data.Maybe.fromMaybe mempty, rhs: Data.Foldable.fold}
    - warn: {lhs: Data.Maybe.fromMaybe 0, rhs: sum}
    - warn: {lhs: Data.Maybe.fromMaybe 1, rhs: product}
    - warn: {lhs: Data.Maybe.fromMaybe empty, rhs: Data.Foldable.asum}
    - warn: {lhs: Data.Maybe.fromMaybe mzero, rhs: Data.Foldable.msum}
    - warn: {lhs: Data.Either.fromRight mempty, rhs: Data.Foldable.fold}
    - warn: {lhs: Data.Either.fromRight False, rhs: or}
    - warn: {lhs: Data.Either.fromRight True, rhs: and}
    - warn: {lhs: Data.Either.fromRight 0, rhs: sum}
    - warn: {lhs: Data.Either.fromRight 1, rhs: product}
    - warn: {lhs: Data.Either.fromRight empty, rhs: Data.Foldable.asum}
    - warn: {lhs: Data.Either.fromRight mzero, rhs: Data.Foldable.msum}
    - warn: {lhs: if f x then Just x else Nothing, rhs: mfilter f (Just x)}
    - hint: {lhs: maybe (pure ()), rhs: traverse_, note: IncreasesLaziness}
    - hint: {lhs: fromMaybe (pure ()), rhs: sequenceA_, note: IncreasesLaziness}
    - hint: {lhs: fromRight (pure ()), rhs: sequenceA_, note: IncreasesLaziness}
    - hint: {lhs: "[fst x, snd x]", rhs: Data.Bifoldable.biList x}
    - hint: {lhs: "\\(x, y) -> [x, y]", rhs: Data.Bifoldable.biList, note: IncreasesLaziness}
    - hint: {lhs: const mempty, rhs: mempty}
    - hint: {lhs: \x -> mempty, rhs: mempty, name: Redundant lambda}

# hints that use the 'extra' library
- group:
    name: extra
    enabled: false
    rules:
    - warn: {lhs: fmap concat (forM a b), rhs: concatForM a b}
    - warn: {lhs: concat <$> forM a b, rhs: concatForM a b}
    - warn: {lhs: fmap concat (forM_ a b), rhs: concatForM_ a b}
    - warn: {lhs: concat <$> forM_ a b, rhs: concatForM_ a b}
    - warn: {lhs: "maybe (pure ()) b a", rhs: "whenJust a b"}
    - warn: {lhs: "maybe (return ()) b a", rhs: "whenJust a b"}
    - warn: {lhs: "maybeM (pure ()) b a", rhs: "whenJustM a b"}
    - warn: {lhs: "maybeM (return ()) b a", rhs: "whenJustM a b"}
    - warn: {lhs: "if a then Just <$> b else pure Nothing", rhs: "whenMaybe a b"}
    - warn: {lhs: "maybe a b =<< c", rhs: "maybeM a b c"}
    - warn: {lhs: "maybeM a pure x", rhs: "fromMaybeM a b"}
    - warn: {lhs: "maybeM a return x", rhs: "fromMaybeM a b"}
    - warn: {lhs: "either a b =<< c", rhs: "eitherM a b c"}
    - warn: {lhs: "fold1M a b >> return ()", rhs: "fold1M_ a b"}
    - warn: {lhs: "fold1M a b >> pure ()", rhs: "fold1M_ a b"}
    - warn: {lhs: "flip concatMapM", rhs: "concatForM"}
    - warn: {lhs: "liftM mconcat (mapM a b)", rhs: "mconcatMapM a b"}
    - warn: {lhs: "ifM a b (return ())", rhs: "whenM a b"}
    - warn: {lhs: "ifM a (return ()) b", rhs: "unlessM a b"}
    - warn: {lhs: "ifM a (return True) b", rhs: "(||^) a b"}
    - warn: {lhs: "ifM a b (return False)", rhs: "(&&^) a b"}
    - warn: {lhs: "anyM id", rhs: "orM"}
    - warn: {lhs: "allM id", rhs: "andM"}
    - warn: {lhs: "either id id", rhs: "fromEither"}
    - warn: {lhs: "either (const Nothing) Just", rhs: "eitherToMaybe"}
    - warn: {lhs: "either (Left . a) Right", rhs: "mapLeft a"}
    - warn: {lhs: "atomicModifyIORef a (\\ v -> (b v, ()))", rhs: "atomicModifyIORef_ a b"}
    - warn: {lhs: "atomicModifyIORef' a (\\ v -> (b v, ()))", rhs: "atomicModifyIORef'_ a b"}
    - warn: {lhs: "null (intersect a b)", rhs: "disjoint a b"}
    - warn: {lhs: "[minBound .. maxBound]", rhs: "enumerate"}
    - warn: {lhs: "zipWithFrom (,)", rhs: "zipFrom"}
    - warn: {lhs: "zip [i..]", rhs: "zipFrom i"}
    - warn: {lhs: "zipWith f [i..]", rhs: "zipWithFrom f i"}
    - warn: {lhs: "dropWhile isSpace", rhs: "trimStart"}
    - warn: {lhs: "dropWhileEnd isSpace", rhs: "trimEnd"}
    - warn: {lhs: "trimEnd (trimStart a)", rhs: "trim a"}
    - warn: {lhs: "map toLower", rhs: "lower"}
    - warn: {lhs: "map toUpper", rhs: "upper"}
    - warn: {lhs: "mergeBy compare", rhs: "merge"}
    - warn: {lhs: "breakEnd (not . a)", rhs: "spanEnd a"}
    - warn: {lhs: "spanEnd (not . a)", rhs: "breakEnd a"}
    - warn: {lhs: "mconcat (map a b)", rhs: "mconcatMap a b"}
    - warn: {lhs: "fromMaybe b (stripPrefix a b)", rhs: "dropPrefix a b"}
    - warn: {lhs: "fromMaybe b (stripSuffix a b)", rhs: "dropSuffix a b"}
    - warn: {lhs: "nubSortBy compare", rhs: "nubSort"}
    - warn: {lhs: "nubSortBy (compare `on` a)", rhs: "nubSortOn a"}
    - warn: {lhs: "nubOrdBy compare", rhs: "nubOrd"}
    - warn: {lhs: "\\a -> (a, a)", rhs: "dupe"}
    - warn: {lhs: "showFFloat (Just a) b \"\"", rhs: "showDP a b"}
    - warn: {lhs: "readFileEncoding utf8", rhs: "readFileUTF8"}
    - warn: {lhs: "withFile a ReadMode hGetContents'", rhs: "readFile' a"}
    - warn: {lhs: "readFileEncoding' utf8", rhs: "readFileUTF8'"}
    - warn: {lhs: "withBinaryFile a ReadMode hGetContents'", rhs: "readFileBinary' a"}
    - warn: {lhs: "writeFileEncoding utf8", rhs: "writeFileUTF8"}
    - warn: {lhs: "head $ x ++ [y]", rhs: "headDef y x"}
    - warn: {lhs: "last $ x : y", rhs: "lastDef x y"}
    - warn: {lhs: "drop 1", rhs: "drop1"}
    - warn: {lhs: "dropEnd 1", rhs: "dropEnd1"}

# hints that will be enabled in future
- group:
    name: future
    enabled: false
    rules:
    - warn: {lhs: return, rhs: pure}

- group:
    name: dollar
    enabled: false
    imports:
    - package base
    rules:
    - warn: {lhs: a $ b $ c, rhs: a . b $ c}

- group:
    # These hints are same if all matched functions are monomorphic, or polymorphic, but don't have adhoc polymorphism
    name: monomorphic
    enabled: false
    imports:
    - package base
    rules:
    - warn: {lhs: if c then f x else f y, rhs: f (if c then x else y), note: IncreasesLaziness, name: Too strict if}
    - hint: {lhs: maybe (f x) (f . g), rhs: f . maybe x g, note: IncreasesLaziness, name: Too strict maybe}
    - hint: {lhs: maybe (f x) f y, rhs: f (Data.Maybe.fromMaybe x y), note: IncreasesLaziness, name: Too strict maybe}

- group:
    name: codeworld
    enabled: false
    imports:
    - package base
    - package codeworld-api
    rules:
    - warn: {lhs: "pictures [ p ]", rhs: p, name: Evaluate}
    - warn: {lhs: "pictures [ p, q ]", rhs: p & q, name: Evaluate}
    - hint: {lhs: foldl1 (&), rhs: pictures}
    - hint: {lhs: foldl (&) blank, rhs: pictures}
    - hint: {lhs: foldl' (&) blank, rhs: pictures}
    - hint: {lhs: foldr' (&) blank, rhs: pictures}
    - hint: {lhs: foldr (&) blank, rhs: pictures}
    - hint: {lhs: foldr1 (&), rhs: pictures}
    - hint: {lhs: scaled x x, rhs: dilated x}
    - hint: {lhs: scaledPoint x x, rhs: dilatedPoint x}
    - warn: {lhs: "brighter (- a)", rhs: "duller a"}
    - warn: {lhs: "lighter (- a)", rhs: "darker a"}
    - warn: {lhs: "duller (- a)", rhs: "brighter a"}
    - warn: {lhs: "darker (- a)", rhs: "lighter a"}

- group:
    name: teaching
    enabled: false
    imports:
    - package base
    rules:
    - hint: {lhs: "x /= []", rhs: not (null x), name: Use null}
    - hint: {lhs: "[] /= x", rhs: not (null x), name: Use null}
    - hint: {lhs: "not (x || y)", rhs: "not x && not y", name: Apply De Morgan law}
    - hint: {lhs: "not (x && y)", rhs: "not x || not y", name: Apply De Morgan law}
    - hint: {lhs: "[ f x | x <- l ]", rhs: map f l}
    - warn: {lhs: foldr f c (reverse x), rhs: foldl (flip f) c x, name: Use left fold instead of right fold}
    - warn: {lhs: foldr1 f (reverse x), rhs: foldl1 (flip f) x, name: Use left fold instead of right fold}
    - warn: {lhs: foldl f c (reverse x), rhs: foldr (flip f) c x, note: IncreasesLaziness, name: Use right fold instead of left fold}
    - warn: {lhs: foldl1 f (reverse x), rhs: foldr1 (flip f) x, note: IncreasesLaziness, name: Use right fold instead of left fold}
    - warn: {lhs: foldr' f c (reverse x), rhs: foldl' (flip f) c x, name: Use left fold instead of right fold}
    - warn: {lhs: foldl' f c (reverse x), rhs: foldr (flip f) c x, note: IncreasesLaziness, name: Use right fold instead of left fold}

- group:
    # used for tests, enabled when testing this file
    name: testing
    enabled: false
    rules:
    - warn: {lhs: "[issue766| |]", rhs: "mempty", name: "Use mempty"}

# <TEST>
# yes = concat . map f -- concatMap f
# yes = foo . bar . concat . map f . baz . bar -- concatMap f . baz . bar
# yes = map f (map g x) -- map (f . g) x
# yes = concat.map (\x->if x==e then l' else [x]) -- concatMap (\x->if x==e then l' else [x])
# yes = f x where f x = concat . map head -- concatMap head
# yes = concat . map f . g -- concatMap f . g
# yes = concat $ map f x -- concatMap f x
# yes = "test" ++ concatMap (' ':) ["of","this"] -- unwords ("test":["of","this"])
# yes = if f a then True else b -- f a || b
# yes = not (a == b) -- a /= b
# yes = not (a /= b) -- a == b
# yes = not . (a ==) -- (a /=)
# yes = not . (== a) -- (/= a)
# yes = not . (a /=) -- (a ==)
# yes = not . (/= a) -- (== a)
# yes = if a then 1 else if b then 1 else 2 -- if a || b then 1 else 2
# no  = if a then 1 else if b then 3 else 2
# yes = a >>= return . bob -- a Data.Functor.<&> bob
# yes = return . bob =<< a -- bob <$> a
# yes = m alice >>= pure . b -- m alice Data.Functor.<&> b
# yes = pure .b =<< m alice -- b <$> m alice
# yes = asciiCI "hi" *> pure Hi -- asciiCI "hi" Data.Functor.$> Hi
# yes = asciiCI "bye" *> return Bye -- asciiCI "bye" Data.Functor.$> Bye
# yes = pure x <* y -- x Data.Functor.<$ y
# yes = return x <* y -- x Data.Functor.<$ y
# yes = const x <$> y -- x <$ y
# yes = pure alice <$> [1, 2] -- alice <$ [1, 2]
# yes = return alice <$> "Bob" -- alice <$ "Bob"
# yes = Just a <&> const b -- Just a Data.Functor.$> b
# yes = [a,b] <&> pure c -- [a,b] Data.Functor.$> c
# yes = Hi <&> return bye -- Hi Data.Functor.$> bye
# yes = (x !! 0) + (x !! 2) -- head x
# yes = if b < 42 then [a] else [] -- [a | b < 42]
# no  = take n (foo xs) == "hello"
# yes = head (reverse xs) -- last xs
# yes = reverse xs `isPrefixOf` reverse ys -- isSuffixOf xs ys
# no = putStrLn $ show (length xs) ++ "Test"
# yes = ftable ++ map (\ (c, x) -> (toUpper c, urlEncode x)) ftable -- Data.Bifunctor.bimap toUpper urlEncode
# yes = map (\(a,b) -> a) xs -- fst
# yes = map (\(a,_) -> a) xs -- fst
# yes = readFile $ args !! 0 -- head args
# yes = if Debug `elem` opts then ["--debug"] else [] -- ["--debug" | Debug `elem` opts]
# yes = if nullPS s then return False else if headPS s /= '\n' then return False else alter_input tailPS >> return True \
#     -- if nullPS s || (headPS s /= '\n') then return False else alter_input tailPS >> return True
# yes = if foo then do stuff; moreStuff; lastOfTheStuff else return () \
#     -- Control.Monad.when foo $ do stuff ; moreStuff ; lastOfTheStuff
# yes = if foo then stuff else return () -- Control.Monad.when foo stuff
# yes = foo $ \(a, b) -> (a, y + b) -- Data.Bifunctor.second ((+) y)
# no  = foo $ \(a, b) -> (a, a + b)
# yes = map (uncurry (+)) $ zip [1 .. 5] [6 .. 10] -- zipWith (curry (uncurry (+))) [1 .. 5] [6 .. 10]
# yes = curry (uncurry (+)) -- (+)
# yes = fst foo .= snd foo -- uncurry (.=) foo
# yes = fst foo `_ba__'r''` snd foo -- uncurry _ba__'r'' foo
# no = do iter <- textBufferGetTextIter tb ; textBufferSelectRange tb iter iter
# no = flip f x $ \y -> y*y+y
# no = \x -> f x (g x)
# no = foo (\ v -> f v . g)
# yes = concat . intersperse " " -- unwords
# yes = Prelude.concat $ intersperse " " xs -- unwords xs
# yes = concat $ Data.List.intersperse " " xs -- unwords xs
# yes = if a then True else False -- a
# yes = if x then true else False -- x && true
# yes = elem x y -- x `elem` y
# yes = foo (elem x y) -- x `elem` y
# no  = x `elem` y
# no  = elem 1 [] : []
# test a = foo (\x -> True) -- const True
# test a = foo (\_ -> True) -- const True
# test a = foo (\x -> x) -- id
# h a = flip f x (y z) -- f (y z) x
# h a = flip f x $ y z
# yes x = case x of {True -> a ; False -> b} -- if x then a else b
# yes x = case x of {False -> a ; _ -> b} -- if x then b else a
# no = const . ok . toResponse $ "saved"
# yes = case x z of Nothing -> y; Just pat -> pat -- Data.Maybe.fromMaybe y (x z)
# yes = if p then s else return () -- Control.Monad.when p s
# warn = a $$$$ b $$$$ c ==> a . b $$$$$ c
# yes = when (not . null $ asdf) -- unless (null asdf)
# yes = (foo . bar . when) (not . null $ asdf) -- (foo . bar) (unless (null asdf))
# yes = id 1 -- 1
# yes = case concat (map f x) of [] -> [] -- concatMap f x
# yes = [v | v <- xs] -- xs
# no  = [Left x | Left x <- xs]
# when p s = if p then s else return ()
# no = x ^^ 18.5
# instance Arrow (->) where first f = f *** id
# yes = fromInteger 12 -- 12
# import Prelude hiding (catch); no = catch
# import Control.Exception as E; no = E.catch
# main = do f; putStrLn $ show x -- print x
# main = map (writer,) $ map arcObj $ filter (rdfPredEq (Res dctreferences)) ts -- map ((writer,) . arcObj) (filter (rdfPredEq (Res dctreferences)) ts)
# h x y = return $! (x, y) -- return (x, y)
# h x y = return $! x
# getInt = do { x <- readIO "0"; return $! (x :: Int) }
# foo = evaluate [12] -- return [12]
# test = \ a -> f a >>= \ b -> return (a, b)
# fooer input = catMaybes . map Just $ input -- mapMaybe Just
# yes = mapMaybe id -- catMaybes
# foo = magic . isLeft $ fmap f x -- magic (isLeft x)
# foo = (bar . baz . magic . isRight) (fmap f x) -- (bar . baz . magic) (isRight x)
# main = print $ map (\_->5) [2,3,5] -- const 5
# main = head $ drop n x -- x !! max 0 n
# main = head $ drop (-3) x -- x
# main = head $ drop 2 x -- x !! 2
# main = foo . bar . baz . head $ drop 2 x -- (foo . bar . baz) (x !! 2)
# main = drop 0 x -- x
# main = take 0 x -- []
# main = take (-5) x -- []
# main = take (-y) x
# main = take 4 x
# main = let (first, rest) = (takeWhile p l, dropWhile p l) in rest -- span p l
# main = let (first, rest) = (take n l, drop n l) in rest -- splitAt n l
# main = fst (splitAt n l) -- take n l
# main = snd $ splitAt n l -- drop n l
# main = map $ \ d -> ([| $d |], [| $d |])
# pairs (x:xs) = map (x,) xs ++ pairs xs
# {-# ANN foo "HLint: ignore" #-};foo = map f (map g x) -- @Ignore ???
# {-# HLINT ignore foo #-};foo = map f (map g x) -- @Ignore ???
# yes = fmap lines $ abc 123 -- lines <$> abc 123
# no = fmap lines $ abc $ def 123
# test = foo . not . not -- id
# test = map (not . not) xs -- id
# used = not . not . any (`notElem` special) . fst . derives -- any (`notElem` special) . fst . derives
# test = foo . id . map -- map
# test = food id xs
# yes = baz baz >> return () -- Control.Monad.void (baz baz)
# no = foo >>= bar >>= something >>= elsee >> return ()
# no = f (#) x
# data Pair = P {a :: !Int}; foo = return $! P{a=undefined}
# data Pair = P {a :: !Int}; foo = return $! P undefined
# foo = return $! Just undefined -- return (Just undefined)
# foo = return $! (a,b) -- return (a,b)
# foo = return $! 1
# foo = return $! "test"
# bar = [x | (x,_) <- pts]
# return' x = x `seq` return x
# foo = last (sortBy (compare `on` fst) xs) -- maximumBy (compare `on` fst) xs
# g = \ f -> parseFile f >>= (\ cu -> return (f, cu))
# foo = bar $ \(x,y) -> x x y
# foo = (\x -> f x >>= g) -- f Control.Monad.>=> g
# foo = (\f -> h f >>= g) -- h Control.Monad.>=> g
# foo = (\f -> h f >>= f)
# foo = bar $ \x -> [x,y]
# foo = bar $ \x -> [z,y] -- const [z,y]
# f condition tChar tBool = if condition then _monoField tChar else _monoField tBool
# foo = maybe Bar{..} id -- Data.Maybe.fromMaybe Bar{..}
# foo = (\a -> Foo {..}) 1
# foo = zipWith SymInfo [0 ..] (repeat ty) -- map (`SymInfo` ty) [0 ..] @NoRefactor
# foo = zipWith (SymInfo q) [0 ..] (repeat ty) -- map (( \ x_ -> SymInfo q x_ ty)) [0 .. ] @NoRefactor
# f rec = rec
# mean x = fst $ foldl (\(m, n) x' -> (m+(x'-m)/(n+1),n+1)) (0,0) x
# {-# LANGUAGE TypeApplications #-} \
# foo = id @Int
# {-# LANGUAGE TypeApplications #-} \
# foo = const @_ @SomeException
# foo = id 12 -- 12
# yes = foldr (\ curr acc -> (+ 1) curr : acc) [] -- map (\ curr -> (+ 1) curr)
# yes = foldr (\ curr acc -> curr + curr : acc) [] -- map (\ curr -> curr + curr)
# no = foo $ (,) x $ do {this is a test; and another test}
# no = sequence (return x)
# no = sequenceA (pure a)
# {-# LANGUAGE QuasiQuotes #-}; no = f (\url -> [hamlet|foo @{url}|])
# yes = f ((,) x) -- (x,)
# yes = f ((,) (2 + 3)) -- (2 + 3,)
# instance Class X where method = map f (map g x) -- map (f . g) x
# instance Eq X where x == y = compare x y == EQ
# issue1055 = map f ((sort . map g) xs)
# issue1049 = True `elem` xs -- or xs
# issue1049 = elem True -- or
# issue1062 = bar (\(f, x) -> baz () () . f $ x) -- uncurry ((.) (baz () ()))
# issue1058 n = [] ++ issue1058 (n+1) -- issue1058 (n+1)
# issue1183 = (a >= 'a') && a <= 'z' -- isAsciiLower a
# issue1183 = (a >= 'a') && (a <= 'z') -- isAsciiLower a

# import Prelude \
# yes = flip mapM -- Control.Monad.forM
# import Control.Monad \
# yes = flip mapM -- forM
# import Control.Monad(forM) \
# yes = flip mapM -- forM
# import Control.Monad(forM_) \
# yes = flip mapM -- Control.Monad.forM
# import qualified Control.Monad \
# yes = flip mapM -- Control.Monad.forM
# import qualified Control.Monad as CM \
# yes = flip mapM -- CM.forM
# import qualified Control.Monad as CM(forM,filterM) \
# yes = flip mapM -- CM.forM
# import Control.Monad as CM(forM,filterM) \
# yes = flip mapM -- forM
# import Control.Monad hiding (forM) \
# yes = flip mapM -- Control.Monad.forM
# import Control.Monad hiding (filterM) \
# yes = flip mapM -- forM
# import qualified Data.Text.Lazy as DTL \
# main = DTL.concat $ map (`DTL.snoc` '-') [DTL.pack "one", DTL.pack "two", DTL.pack "three"]
# import Text.Blaze.Html5.Attributes as A \
# main = A.id (stringValue id')
# import Prelude((==)) \
# import qualified Prelude as P \
# main = P.length xs == 0 -- P.null xs
# main = hello .~ Just 12 -- hello ?~ 12
# foo = liftIO $ window `on` deleteEvent $ do a; b
# no = sort <$> f input `shouldBe` sort <$> x
# sortBy (comparing length) -- sortOn length
# myJoin = on $ child ^. ChildParentId ==. parent ^. ParentId
# foo = typeOf (undefined :: Foo Int) -- typeRep (Proxy :: Proxy (Foo Int))
# foo = typeOf (undefined :: a) -- typeRep (Proxy :: Proxy a)
# {-# RULES "Id-fmap-id" forall (x :: Id a). fmap id x = x #-}
# import Data.Map (fromList) \
# fromList [] -- Data.Map.empty
# import Data.Map.Lazy (fromList) \
# fromList [] -- Data.Map.Lazy.empty
# import Data.Map.Strict (fromList) \
# fromList [] -- Data.Map.Strict.empty
# test953 = for [] $ \n -> bar n >>= \case {Just n  -> pure (); Nothing -> baz n}
# f = map (flip (,) "a") "123" -- (,"a")
# f = map ((,) "a") "123" -- ("a",)
# test979 = flip Map.traverseWithKey blocks \k v -> lots_of_code_goes_here
# infixl 4 <*! \
# test993 = f =<< g <$> x <*! y
# {-# LANGUAGE QuasiQuotes #-} \
# test = [issue766| |] -- mempty
# {-# LANGUAGE QuasiQuotes #-} \
# test = [issue766| x |]
# </TEST>