7

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?

Will Ness
  • 62,652
  • 8
  • 86
  • 167
Penguino
  • 2,008
  • 1
  • 12
  • 19
  • 4
    one part of (the construction part) *is* yes - but you can use those also for pattern-matching and there of course it is not a function any more ;) – Carsten May 26 '21 at 09:51
  • 2
    It is a function in the sense you describe yes. You can further confirm this by e.g. typing `:t Just` in ghci. – michid May 26 '21 at 09:55
  • @Carsten I haven't done a lot with pattern-matching yet, but as far as I can see, one can us pattern matching both when defining a function (so to do with functions) and when defining a class instance (so to do with types). Can you explain further or give me a reference for how they differ re pattern-matching? Thanks – Penguino May 26 '21 at 09:55
  • @michid So is there any sense in which it is *not* a function, or can I do anything to a type constructor that I can do to a function? – Penguino May 26 '21 at 09:57
  • 1
    well if you do `case mySentence of (Sentence s v o) -> ...` for example - here `Sentence` is used for pattern-matching and you cannot insert a function there (well there are extensions ... but on a basic level you cannot) – Carsten May 26 '21 at 10:01
  • yes, that makes sense. Thanks. – Penguino May 26 '21 at 10:04
  • @Penguino, pattern matching as discussed in other comments: you cannot use "normal" functions in pattern matching but you can use type constructors. – michid May 26 '21 at 12:38

1 Answers1

11

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.

leftaroundabout
  • 101,764
  • 3
  • 156
  • 291
  • Might be worth clarifying being an injective function is not the sole reason you can pattern match. Perhaps a data constructor is a function that can *create* a value, rather than just *returning* a value? – chepner May 26 '21 at 13:43
  • 3
    @chepner, I think it's best to think of a constructor as at least two separate things: a function for constructing values and a pattern for matching on them. Which you get is determined syntactically by where it appears. – dfeuer May 26 '21 at 13:56
  • It's also surjective in the sense that every value of type `SentenceTC` is returned by `SentenceDC` for some arguments. – Alexey Romanov May 27 '21 at 09:28
  • 1
    @AlexeyRomanov in this case yes (ignoring ⊥, of course), but that's because there is only one constructor in the example. In general, constructors are not surjective. – leftaroundabout May 27 '21 at 09:46