Reputation: 8521
In Chapter 7, "More functional patterns" of Haskell Programming from First Principles, given the following data types:
newtype Username = Username String
newtype AccountNumber = AccountNumber Integer
data User = UnregisteredUser
| RegisteredUser Username AccountNumber
it defines the following function:
printUser :: User -> IO ()
printUser UnregisteredUser = putStrLn "UnregisteredUser"
printUser (RegisteredUser (Username name)
(AccountNumber acctNum))
= putStrLn $ name ++ " " ++ show acctNum
But experimenting with the code, I realized that I can also define de printUser
function as:
printUser :: User -> IO ()
printUser user = case user of
UnregisteredUser -> putStrLn "UnregisteredUser"
(RegisteredUser (Username name)
(AccountNumber acctNum)) -> putStrLn $ name ++ " " ++ show acctNum
I'm trying to understand why it is possible to use two such different styles. Is there some design motivation behind it? Or, an explanation like, "first style is preferred in such and such situations, whereas the second style of definition would better be used when ..."? (I mean, anything other than a vague 'readability'-motivated explanation?)
Upvotes: 3
Views: 248
Reputation: 30227
The answer to this is in the following, highly recommended paper:
In section 4.4 they talk about this:
4.4 Declaration style vs. expression style
As our discussions evolved, it became clear that there were two different styles in which functional programs could be written: “declaration style” and “expression style”. [...]
The declaration style attempts, so far as possible, to define a function by multiple equations, each of which uses pattern matching and/or guards to identify the cases it covers. In contrast, in the expression style a function is built up by composing expressions together to make bigger expressions. [...]
It took some while to identify the stylistic choice as we have done here, but once we had done so, we engaged in furious debate about which style was “better.” An underlying assumption was that if possible there should be “just one way to do something,” so that, for example, having both let and where would be redundant and confusing.
In the end, we abandoned the underlying assumption, and provided full syntactic support for both styles. This may seem like a classic committee decision, but it is one that the present authors believe was a fine choice, and that we now regard as a strength of the language.
So the reason is just that when Haskell was being invented, different authors had different preferences, and they just threw both styles into the language. So use the one you like.
Declaration style does seem to be more popular, although I'd say most people use a mix.
Upvotes: 10
Reputation: 531345
In fact, a piecewise function definition is just syntactic sugar for the definition that uses a case
expression. A simpler example:
f (Left l) = eL
f (Right r) = eR
-- ... desugars to:
f x = case x of
Left l -> eL
Right r -> eR
Upvotes: 1
Reputation: 791
The first format is useful if you want to destruct an argument with single constructor because it makes your functions shorter and cleaner. The second version is preferred when you have multiple constructors because if you have to change the name of the function you only have to do it in one place.
Upvotes: 1