The following two definitions are equivalent. Which one do you like more?
(Best compared in your editor of choice, with syntax highlighting.)
With higher order control flow functions:
initializePostgresCluster :: FilePath -> IO PostgresClusterID
initializePostgresCluster location = do
maybeCurrentUserName <- lookupEnv "USER"
currentUserName <- maybe (throwIO DeploymentException) return maybeCurrentUserName
locationIsOccupied <- doesPathExist location
when locationIsOccupied $ throwIO DeploymentException
locationIsInDirectory <- doesDirectoryExist (dropFileName location)
when (not locationIsInDirectory) $ throwIO DeploymentException
callProcess "initdb" ["--pgdata", location, "--username", currentUserName]
status <- validatePostgresClusterLocation location
either throwIO return status
With case statements:
initializePostgresCluster :: FilePath -> IO PostgresClusterID
initializePostgresCluster location = do
maybeCurrentUserName <- lookupEnv "USER"
currentUserName <- case maybeCurrentUserName of
Nothing -> throwIO DeploymentException
Just x -> return x
locationIsOccupied <- doesPathExist location
case locationIsOccupied of
True -> throwIO DeploymentException
False -> do
locationIsInDirectory <- doesDirectoryExist (dropFileName location)
case locationIsInDirectory of
False -> throwIO DeploymentException
True -> do
callProcess "initdb" ["--pgdata", location, "--username", currentUserName]
status <- validatePostgresClusterLocation location
case status of
Left e -> throwIO e
Right postgresClusterID -> return postgresClusterID
Please make up your mind before reading further, lest you be biased by my argument.
Discussion:
For higher order control flow functions:
- Code is narrower since no indentation is needed. (Or, indeed, allowed.)
— But who cares, given modern display technology?
-
Code is more concise.
-
Reads like plain English.
For case statements:
-
Less cognitively demanding: no need to remember the shape of the type and mentally figure out which argument handles which case.
-
Proliferation of control flow functions is avoided, reducing long term memory recall expenses and hoogling delays.
-
Indentation makes code structure clear from a glance.
-
Parentheses are avoided, thus editing is easier.
-
Syntax highlighting helps readability more since the code is not a uniform wall of value identifiers but a pleasant tableau of value identifiers, data constructors, keywords and arrows. Unfortunately, Discourse does not seem to know Haskell, so please copy to your editor of choice in order to appreciate the difference.
-
Adding more statements (for example, logging) to a case is straightforward.
Against case statements:
- Adding another control flow fork in the middle of a block requires adjusting the indentation of all the consecutive lines, resulting in a spurious diff.