Skip to content
Tech News
← Back to articles

Prefer do notation over Applicative operators when assembling records (2024)

read original get Haskell Do Notation Guide → more articles
Why This Matters

This article emphasizes the advantages of using do notation over Applicative operators when assembling records in Haskell, highlighting improved readability and ergonomics. It encourages developers to adopt do notation for clearer, more maintainable code, especially in interactive scenarios like user input collection. This guidance can influence best practices in Haskell programming, making code more accessible and easier to understand for both new and experienced developers.

Key Takeaways

This is a short post explaining why you should prefer do notation when assembling a record, instead of using Applicative operators (i.e. (<$>) / (<*>) ). This advice applies both for type constructors that implement Monad (e.g. IO ) and also for type constructors that implement Applicative but not Monad (e.g. the Parser type constructor from the optparse-applicative package). The only difference is that in the latter case you would need to enable the ApplicativeDo language extension.

The guidance is pretty simple. Instead of doing this:

data Person = Person { firstName :: String , lastName :: String } getPerson :: IO Person getPerson = Person <$> getLine <*> getLine

… you should do this:

{-# LANGUAGE RecordWildCards #-} {-# OPTIONS_GHC -Werror=missing-fields #-} data Person = Person { firstName :: String , lastName :: String } getPerson :: IO Person getPerson = do firstName <- getLine lastName <- getLine return Person { .. }

Why is the latter version better? There are a few reasons.

Ergonomics

It’s more ergonomic to assemble a record using do notation because you’re less pressured to try to cram all the logic into a single expression.

For example, suppose we wanted to explicitly prompt the user to enter their first and last name. The typical way people would do extend the former example using Applicative operators would be something like this:

getPerson :: IO Person getPerson = Person <$> ( putStrLn "Enter your first name:" *> getLine ) <*> ( putStrLn "Enter your last name:" *> getLine )

... continue reading