4

Here is my code:

tell :: (Show a) => [a] -> String  
tell [] = "The list is empty"  
tell (x:[]) = "The list has one element: " ++ show x  
tell (x:y:[]) = "The list has two elements: " ++ show x ++ " and " ++ show y  
tell (x:y:_) = "Other" 

in the last line why can't I change (x:y:_) to [x, y, _]?

And what is the global meaning of _?

Makyen
  • 31,849
  • 12
  • 86
  • 121

4 Answers4

12

in last line why I can't change (x:y:_) to [x, y, _]

They have different meanings:

  • [x, y, _] refers to a list with three elements; the first element is bound to x, the second is bound to y, and the third element is not bound to any name. It's equivalent to (x:y:_:[]).
  • By contrast, (x:y:_) refers to a list with at least two elements, since : is a constructor used to join the first element of a list with the rest of the list; in the above pattern, the first element is bound to x, the second is bound to y, and the rest of the list (which may or may not be empty) is not bound to any name.

And what the global meaning of _?

You shouldn’t really ask two questions in one post, but it’s relevant here, so: _ is just a placeholder when you want to match a particular pattern, but not give it a name. So in the above examples:

  • [x,y,z] will bind each element of a three-element list to the names x, y and z respectively (exactly equivalent to (x:y:z:[])). When you replace z with _, it still matches a three-element list, but does not give the last element a name.
  • (x:y:z) will bind the first two elements of an at-least-two-element list to the names x and y, then binds the rest of the list to z. When you replace z with _, it still matches a list of at least two elements, but does not give the rest of the list a name.
bradrn
  • 8,337
  • 2
  • 22
  • 51
  • Can I use _ like an identifier? – Valentin Grigorev Aug 16 '19 at 00:22
  • @user2923317 I could be wrong, but I don’t believe so — it is simply a placeholder for when you want to match a particular pattern, but don’t want to give a name to one (or more) values in the pattern. – bradrn Aug 16 '19 at 00:26
  • Also thanks @luqui for the edit — I can’t believe I got that wrong! – bradrn Aug 16 '19 at 00:26
  • 1
    @user2923317 it's not just a variable name like in, say, javascript. In patterns it means "ignore this". In expressions (in GHC at least) it means a "typed hole". That is, it will fail to compile but the error message will tell you what type it was expecting in that position. – luqui Aug 16 '19 at 03:31
9

You can change (x : y : _) to [x, y, _], but it won't mean the same thing. [x, y, _] is equivalent to (x : y : _ : []), i.e. a list of exactly three elements (the first two of which are bound to x and y).

Similarly, x : y is a list whose head (first element) is x and whose tail (remaining elements) is y, but [x, y] is a list of exactly two elements.

I'm not sure what you mean by "global meaning", but _ is a wildcard pattern that matches any value, but doesn't bind it to a name.

melpomene
  • 84,125
  • 8
  • 85
  • 148
  • so, if I load moule with function the error is about i don't have case to list with any length? – Valentin Grigorev Aug 16 '19 at 00:24
  • 1
    That is correct @user2923317. If you use `[x,y,_]`, along with the other patterns in your question, that gives you patterns to match 0-, 1-, 2- and 3-element lists, but you don’t have any patterns to match longer ones! – bradrn Aug 16 '19 at 00:28
3

In patterns, _ means "match anything here, I don't care about it".

Since underscores are valid characters in identifiers, you can almost treat _ as equivalent to any other variable name that you just happen not to use on the right hand side. (The only difference is when you write multiple _ in the same pattern; in that case each _ is treated as separately matching anything, whereas using a normal variable multiple times in a pattern gives an error about conflicting definitions for the variable)

Since you only use a single _ in each pattern, we can see that it'll do the same thing if we just invent a new variable and use that.

So tell (x:y:_) = ... is the same as tell (x:y:z) = ....

OTOH tell [x, y, _] = ... is the same as tell [x, y, z] = .... This is completely different than the above. In x:y:z, the left argument of each : is an item in the list and the right argument is a list containing the rest of the items1. But in [x, y, z], each of x, y, and z are items in the list.

The square bracket list syntax [a, b, c, d, ...] does not allow you to refer to the tail of list. Each of the comma-separated expressions are items contained in the list, and the list has exactly the number of elements you wrote. You cannot use square bracket list syntax to write a pattern that matches a list with an arbitrary number of elements.

So the pattern (x:y:_) (same as (x:y:z)) matches a list with at least 2 elements, with the _ matching the zero-or-more remaining elements of the list after the first 2.

The pattern [x, y, _] matches a list with exactly 3 elements, with the _ matching the third element.


1 The : operator is right associative, so x:y:z means x:(y:z); x and y are both left arguments to different : and thus items, while z is a right argument to : and thus a list.


To answer your question about the "global meaning of _", it doesn't really have a global meaning. It means different things in different contexts, which can be a little confusing. They all generally relate to the idea of "don't know" or "don't care", but they're not really otherwise connected.

The above discussion was about the meaning of _ in patterns.

In expressions, you can use _ as a placeholder to indicate "I don't know what goes here yet". If you compile code containing _ as an expression, GHC will give you an error, but it will try to figure out the type of what could go there and give you some information about it in the error message. I very frequently start writing a function by writing out a basic "shape" full of _, and then letting the compiler tell me what kinds of thing I need to come up with to fill in the gaps.

For example, compiling this:

mymap :: (a -> b) -> [a] -> [b]
mymap f [] = []
mymap f (x:xs) = _ : _

Produces this:

/tmp/foo.hs:4:18: error:
    • Found hole: _ :: b
      Where: ‘b’ is a rigid type variable bound by
               the type signature for:
                 mymap :: forall a b. (a -> b) -> [a] -> [b]
               at /tmp/foo.hs:2:1-31
    • In the first argument of ‘(:)’, namely ‘_’
      In the expression: _ : _
      In an equation for ‘mymap’: mymap f (x : xs) = _ : _
    • Relevant bindings include
        xs :: [a] (bound at /tmp/foo.hs:4:12)
        x :: a (bound at /tmp/foo.hs:4:10)
        f :: a -> b (bound at /tmp/foo.hs:4:7)
        mymap :: (a -> b) -> [a] -> [b] (bound at /tmp/foo.hs:3:1)
  |
4 | mymap f (x:xs) = _ : _
  |                  ^

/tmp/foo.hs:4:22: error:
    • Found hole: _ :: [b]
      Where: ‘b’ is a rigid type variable bound by
               the type signature for:
                 mymap :: forall a b. (a -> b) -> [a] -> [b]
               at /tmp/foo.hs:2:1-31
    • In the second argument of ‘(:)’, namely ‘_’
      In the expression: _ : _
      In an equation for ‘mymap’: mymap f (x : xs) = _ : _
    • Relevant bindings include
        xs :: [a] (bound at /tmp/foo.hs:4:12)
        x :: a (bound at /tmp/foo.hs:4:10)
        f :: a -> b (bound at /tmp/foo.hs:4:7)
        mymap :: (a -> b) -> [a] -> [b] (bound at /tmp/foo.hs:3:1)
      Valid hole fits include
        mempty :: forall a. Monoid a => a
          with mempty @[b]
          (imported from ‘Prelude’ at /tmp/foo.hs:1:1
           (and originally defined in ‘GHC.Base’))
  |
4 | mymap f (x:xs) = _ : _
  |                      ^

Another use of _ is in type signatures. Here it means "I don't know what this is, you tell me". Since the compiler can infer types anyway most of the time, it will simply tell you in the error message what you should use to fill in the blank.

For example, compiling this:

foo :: Int -> _
foo x = Just x

Produces:

/tmp/foo.hs:2:15: error:
    • Found type wildcard ‘_’ standing for ‘Maybe Int’
      To use the inferred type, enable PartialTypeSignatures
    • In the type signature: foo :: Int -> _
  |
2 | foo :: Int -> _
  |               ^

(You can even use the PartialTypeSignatures language extension to allow GHC to go ahead and use the type it infers to fill in the blanks, instead of treating it as an error)

Ben
  • 68,572
  • 20
  • 126
  • 174
0

Quick summarize:

data [a] = [] | a : [a]

So, to do pattern matching that explain by themselves:

head (x:_) = x

tail (_:xs) = xs

secondElem (_:x:_) = x

wrapInList x = [x]
wrapInList x = x : []
wrapTwoInlist x y = [x, y]
wraptwoInlist x y = x : y : []

unWrapFromList [x] = x

unWrapTwoFromList (x:y:_) = (x,y)

unWrapFromTwoSizeList [x,y] = (x,y)

unWrapFromThreeSizeList [x,y,_] = (x,y)

unWrapFromIDontKnowTheSizeList (x:y:_) = (x,y)

The last two, are the main difference between [x,y,_] and (x:y:_), the first is a three element list, the other you cannot tell.

developer_hatch
  • 15,898
  • 3
  • 42
  • 75