Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is "List" specially handled in Haskell's pattern matching?

I am a newbie in Haskell and hope this question is not silly.

I have seen so much example that when I am having a list, I am able to match and bind "composition element" of the list to individual variable:

listSizeDesc :: [a] -> String
listSizeDesc [] = "Emtpy"
listSizeDesc (x:xs) = "Something inside"

However, I tried to do something like:

foo :: Int -> String
foo 0 = "Zero"
foo (n - 1) = "next number is " ++ show n

It doesn't work.

It seems to me that both (n-1) and (x:xs) describe how the argument is "created" and bind the "component" to an argument. Is the way List matched specially designed for ease of recursion? Coz it seems to me this matching / argument-binding logic doesn't apply to other functions except (:).

like image 944
Adrian Shum Avatar asked Nov 03 '11 09:11

Adrian Shum


People also ask

What is list in Haskell?

In Haskell, lists are a homogenous data structure. It stores several elements of the same type. That means that we can have a list of integers or a list of characters but we can't have a list that has a few integers and then a few characters. And now, a list!

How do you get the first N elements of a list in Haskell?

There is a function in Haskell that takes first n elements of user-supplied list, named take . The syntax is: function-name arg1 arg2 . So, take takes first 1000 elements from an infinite list of numbers from 0 to infinity.


2 Answers

The problem you're encountering is that pattern matching only works with data constructors. A data constructor is in essence very simple; it just takes data values and groups them together in some sort of structure. For example, data Foo = Bar a b simply takes two pieces of data and groups them together under the Foo label. The (:) function you use in your first example is more than just a function; it's a data constructor. It constructs a new list by adding the left argument to the right argument.

Now, pattern matching is merely doing the opposite of this process. It deconstructs a datatype. When you write (x:xs) in your pattern, you're extracting the two pieces of data that the constructor originally stitched together. So all pattern matching does is extract the data that a constructor previously stitched together.

There is one exception: n+k patterns. In Haskell98, you were allowed to use patterns of the form (n+k). This was sort of an arbitrary exception and it was recently removed. If you'd like, you can still use it if you include the NPlusKPatterns language pragma.

like image 179
Jeff Burka Avatar answered Sep 28 '22 04:09

Jeff Burka


The list type is "Sum type" with a constructor, something like:

data List a =
     cons a (List a)
   | nil

You first example is a pattern match on a datatype (with syntactic sugar for :).

Your second example is a pattern match on integers, which are not a datatypye definition. On integer, there is no pattern using your syntax. You may write your example with:

foo :: Int -> String
foo 0 = "Zero"
foo n = "next number is " ++ show (n+1)

On a side note if you encode integers with datatypes like:

data Nat = Zero | Succ Nat deriving (Show)

Then you can use your pattern match as you wanted initially.

foo :: Nat -> String
foo Zero = "Zero"
foo n@Succ(p) = "next number is " ++ show(n)

Here the pattern Succ(p) plays the role of n-1.

like image 42
Pierre Avatar answered Sep 28 '22 04:09

Pierre