hlint : your friendly code critic and adviser

When learning a programming language, won’t it be nice if you can get some instant feedback on your code? Of course, you cannot expect feedback on bugs in your program. But each language has some built in functionality and idioms which a beginner may not be aware of and it will be helpful to get some advise.

If your programming language is haskell, then you are in luck as there is a program called hlint written by Neil Mitchell. You can install it as “cabal install hlint”. You run it and give a haskell file as its argument.

In my code I had

and $ map isDigit numMessageText

hlint suggested (like a friendly tutor), why not

all isDigit numMessageText

I looked up type of *all*

:t all

all :: (a -> Bool) -> [a] -> Bool

In the hugs prelude you have all defined as
all p = and . map p

hlint will also tell you that parenthesis are redundant. In some cases, for the purpose of clarity, you can ignore this advice. But it is good to know that the parenthesis are not needed. hlint will also warn you if your variables are not CamelCased as that seems to be the convention for writing haskell code.

One type of suggestion is the “Eta reduce”:
If you have
count p l = length (filter p l)
hlint will ask, Why not:�count p = length . filter p

Another example: do print “hello”

Error: Redundant do
do print “hello”
Why not:
print “hello”

Now, if you are greedy like me and don’t want to get these nuggets one at a time, you can read the hlint source and get all of them. Of course, you will still use the hlint program on your code. I typically do that before�committing�my code.

I will list only some of the items in the hlint code.

error = putStrLn (show x) ==> print x
error = hGetChar stdin ==> getChar
error = hPrint stdout ==> print

warn = n `rem` 2 == 0 ==> even n
warn = not (even x) ==> odd x

error = concat (map f x) ==> concatMap f x
error “Use map once” = map f (map g x) ==> map (f . g) x
warn = x !! 0 ==> head x
error = take n (repeat x) ==> replicate n x
error = x ++ concatMap (‘ ‘:) y ==> unwords (x:y)
error = head (reverse x) ==> last x
error = head (drop n x) ==> x !! n
error = reverse (tail (reverse x)) ==> init x
error = span (not . p) ==> break p

error = concatMap (++ “\n”) ==> unlines
error = or (map p x) ==> any p x
error = zipWith (,) ==> zip
error = zipWith3 (,,) ==> zip3
warn = length x == 0 ==> null x
error = not (elem x y) ==> notElem x y

error “Redundant ==” = a == True ==> a
warn “Redundant ==” = a == False ==> not a
error “Redundant if” = (if a then True else False) ==> a
error “Redundant if” = (if a then t else (if b then t else f)) ==> if a || b then t else f
error “Use if” = case a of {True -> t; False -> f} ==> if a then t else f

error = sequence (map f x) ==> mapM f x
error = sequence_ (map f x) ==> mapM_ f x
warn = flip mapM ==> forM
error = when (not x) ==> unless x

warn “Use list comprehension” = (if b then [x] else []) ==> [x | b]
error “Evaluate” = fst (x,y) ==> x
error “Evaluate” = f (fst p) (snd p) ==> uncurry f p

warn = log y / log x ==> logBase x y
warn = sin x / cos x ==> tan x

I wouldn’t be surpised if such an utility was available for other language. It should be relatively easy if it is a functional programming language.

Leave a Reply

Your email address will not be published. Required fields are marked *