I am going through "Haskell Programming from first principles" and found an exercise asking if the following [code slightly edited here] was valid:
module Test where
type Subject = String
type Verb = String
type Object = String
data Sentence =
Sentence Subject Verb Object
deriving (Eq, Show)
a1 = Sentence "I" "like" "cheese"
a2 = Sentence "I" "scream"
My expectation was initially that the code would fail, because in the definition of a2
, Sentence
only had two arguments. But found that GHCi was happy to load the module. I did a little experimentation and found that I could now type
a3 = a2 "icecream"
and a3
(typed into GHCi) would print Sentence "I" "scream" "icecream"
. Also, if I inquire the types of a2
I get a2 :: Object -> Sentence
. So if I understand correctly, a2
is behaving exactly like a partially-applied function.
Question therefore is: Is a type constructor really just a function (that returns a type value) in all situations - distinguished from a 'normal' function only in that it has to start with an upper-case character?
First off, what you're talking about here are data constructors, not type constructors. The example happens to contain both a (nullary) type constructor Sentence
and a ternary data constructor Sentence
. To make it clear which is which:
data SentenceTC = SentenceDC Subject Verb Object
SentenceTC
is a type constructor, SentenceDC
is a data constructor.
So, the question is:
Is
SentenceDC
just a function?
and the answer is, it is a function, but not “just” a function. It is specifically an injective function, i.e. every combination of arguments leads to a different result. Because of that, it is always possible to infer which arguments it was, from the resulting SentenceTC
value. And that's what happens when you pattern match on a constructor.
a1Verb :: Verb
a1Verb = case a1 of
Sentence _ v _ -> v
This would not be possible with a general function, like
n' :: Int
n' = abs n
where n = -3
nNew :: Int
nNew = case n' of
abs n -> n -- error, `abs` can not be used as a pattern match
And that wouldn't make sense either, because there are actually two different numbers whose abs
is equal to n'
(namely, -3
and 3
).
But yeah, SentenceDC
is a function, you can do everything with it that you could do with other functions of type String -> String -> String -> SentenceTC
. Not the other way around though: not everything you can do with a data constructor can also be done with a general function of the same type.