
One of the best bad example is the use of boolean as arguments.
Oh, yes. That's a pet peeve of mine. About 99% of boolean arguments should be meaningful two-valued enumerated types. It's literally a one-liner to create such an enumerated type, so there's no excuse.
The documentation effect and type safety provided by two-valued enumerated types is indeed much greater. But one needs a conversion from Bool to the enumeration if one wants to pass the result of a logic operation to the function. What about records with named fields, especially if more options are needed?
data CreateDirectoryOptions = Cons {createParents :: Bool}
createDirectory (Cons {createParents = True}) "dir"
Hummmm.... I think I like this. Something like the following allows a simple way to make the call site concise and provide defaults at the same time. Additional plus -- adding options requires no call-site code changes. --------------------------------------- import Control.Monad -- provide "opts" which should be a labeled record -- of default options class FunctionOptions a where opts :: a -- alias for readability when you don't want to change -- the default options defaults = opts -- create a datatype for each function which needs some flags data CreateDirectoryOptions = CreateDirectoryOptions { createParents :: Bool , barFlag :: Bool , andNow :: Bool , aWholeBunch :: Bool , ofOther :: Bool , seldomEverUsed :: Bool , esotericOptions :: Bool } -- set the flag defaults instance FunctionOptions CreateDirectoryOptions where opts = CreateDirectoryOptions { createParents = False , barFlag = True , andNow = True , aWholeBunch = True , ofOther = False , seldomEverUsed = True , esotericOptions = False } createDirectory :: CreateDirectoryOptions -> FilePath -> IO () createDirectory o path = do when (createParents o) (putStrLn "creating parents") when (barFlag o) (putStrLn "bar flag true") putStrLn ("creating "++path) return () -- readable AND typesafe :-) main = do createDirectory opts{ createParents = True } "foo" createDirectory defaults "baz"