Haskell Code Smells

Common code smells in Haskell and how to fix them.


Partial Functions

1-- ❌ Bad: head can fail
2getFirst :: [a] -> a
3getFirst xs = head xs
4
5-- ✅ Good: Use Maybe
6getFirst :: [a] -> Maybe a
7getFirst [] = Nothing
8getFirst (x:_) = Just x

Not Using Pattern Matching

 1-- ❌ Bad
 2processResult :: Either String Int -> String
 3processResult r = 
 4    if isLeft r 
 5    then fromLeft "" r 
 6    else show (fromRight 0 r)
 7
 8-- ✅ Good
 9processResult :: Either String Int -> String
10processResult (Left err) = err
11processResult (Right val) = show val

Lazy IO

 1-- ❌ Bad: Lazy IO can leak resources
 2readConfig :: FilePath -> IO Config
 3readConfig path = do
 4    contents <- readFile path
 5    return (parse contents)
 6
 7-- ✅ Good: Strict IO
 8import qualified Data.Text.IO as TIO
 9readConfig :: FilePath -> IO Config
10readConfig path = do
11    contents <- TIO.readFile path
12    return (parse contents)

String Instead of Text

1-- ❌ Bad: String is [Char], inefficient
2processName :: String -> String
3processName name = map toUpper name
4
5-- ✅ Good: Use Text
6import qualified Data.Text as T
7processName :: T.Text -> T.Text
8processName = T.toUpper

Not Using Applicative

 1-- ❌ Bad
 2validateUser :: Maybe String -> Maybe Int -> Maybe User
 3validateUser maybeName maybeAge =
 4    case maybeName of
 5        Nothing -> Nothing
 6        Just name -> case maybeAge of
 7            Nothing -> Nothing
 8            Just age -> Just (User name age)
 9
10-- ✅ Good
11validateUser :: Maybe String -> Maybe Int -> Maybe User
12validateUser maybeName maybeAge = User <$> maybeName <*> maybeAge

Related Snippets