10
votes

In order to get a head start into the path of Haskell, I chose the book by one of its creators Hudak. So, I am going through the gentle introduction to Haskell.

I got stuck at trying to understand the following statement:

Technically speaking, formal parameters are also patterns but they never fail to match a value.

From my little but relatively greater habituation with languages like C (or let's broadly say as non-functional languages), I could form that formal parameters are the arguments in the definition of a function. So, suppose there were a function like the following in C:

int func_add(int a, int d) 

then passing a value of some other type like string will be a failure in pattern matching if I am correct. So calling func_add as func_add("trs", 5) is a case of pattern mismatch.

With a heavy possibility of an incorrect understanding or interpretation this similar situation can occur very well in Haskell as well when a piece of code calls a function by passing arguments of different types.

So, why is it said that in Haskell formal parameters are irrefutable pattern matching?

3
In some sense, you can boil it down to "variables are irrefutable, literals are refutable, and combinations are refutable if any part is refutable". For types with only one constructor (like a tuple), a seemingly refutable pattern like (x, y) (or, as it can be rewritten, (,) x y, a refutable literal followed by two irrefutable variables) can be considered irrefutable because after type checking, only values created by (,) are legal.chepner
Ergo, a formal parameter is irrefutable because it is a variable.chepner

3 Answers

7
votes

No, passing a value of some other type is not a failure in the pattern matching. It's a type error, and the code won't even compile. Formal parameters are irrefutable patterns for a well-typed program, which is the only kind of program that the compiler allows you to run.

7
votes

What you describe is not a pattern, it is a type. Haskell has types as well, and these are resolved at compile time. Each time can have several patterns. For example a list is defined as:

data Color = Red | Green | Blue | Other String

Now we can define a function foo:

foo :: Color -> String
foo Red = "Red"
foo Green = "Green"
foo Blue = "Blue"
foo (Other s) = s

The elements in boldface are all patterns. But these are not irrefutable: the first one will check if we have given the function a Red, the second whether we have given it Green, the third if the value is Blue, and finally we have a pattern (Other s) that will match with all Other patterns (regardless what the value of s is), since s is a variable, and a variable is an irrefutable pattern: we do not perform any checks on the value of the string.

Mind that these checks are done at runtime: if we would however call foo "Red", we will get a type error at compile time since the Haskell compiler knows that foo has type Color -> String.

If we would have written:

foo :: Color -> String
foo c = "Some color"
foo Red = "Red"

c is an irrefutable pattern: it will match any Color object, so the second line foo Red will never match, so c is an irrefutable pattern.

2
votes

In Haskell, you can define types in various ways. One of these is to introduce a sum type, like this:

data FooBar = Foo Int | Bar Bool

You could attempt to write a function like this, using pattern matching:

myFunction (Foo x) = x

That would, however, be a partially matched function, and if you try to call it with myFunction (Bar False), you'd get an exception.

You can, on the other hand, also define single-case sum types, like this:

data MyInt = MyInt Int

Here, you can write a function like this:

myFunction' (MyInt x) = x

Here, you're still using pattern matching, but since there's only a single case, this is a complete match. If the calling code compiles, the match can't fail.

The above MyInt is really only a wrapper around Int, so you could say that if you write a function that takes an Int, like this, it's the same sort of pattern matching:

myFunction'' :: Int -> Int
myFunction'' x = x + 42

While Int doesn't have a value constructor that you can use to pattern-match on, x is a pattern that always matches an Int value. Therefore you can say that a function argument is a match that always succeeds.