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.
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.