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