views:

186

answers:

3

Why does the following Haskell script not work as expected?

find :: Eq a => a -> [(a,b)] -> [b]
find k t = [v | (k,v) <- t]

Given find 'b' [('a',1),('b',2),('c',3),('b',4)], the interpreter returns [1,2,3,4] instead of [2,4]. The introduction of a new variable, below called u, is necessary to get this to work:

find :: Eq a => a -> [(a,b)] -> [b]
find k t = [v | (u,v) <- t, k == u]

Does anyone know why the first variant does not produce the desired result?

+8  A: 

The pattern match (k,v) <- t in the first example creates two new local variables v and k that are populated with the contents of the tuple t. The pattern match doesn't compare the contents of t against the already existing variable k, it creates a new variable k (which hides the outer one).

Generally there is never any "variable substitution" happening in a pattern, any variable names in a pattern always create new local variables.

sth
+12  A: 

From the Haskell 98 Report:

As usual, bindings in list comprehensions can shadow those in outer scopes; for example:

[ x | x <- x, x <- x ] = [ z | y <- x, z <- y]

One other point: if you compile with -Wall (or specifically with -fwarn-name-shadowing) you'll get the following warning:

Warning: This binding for `k' shadows the existing binding
           bound at Shadowing.hs:4:5

Using -Wall is usually a good idea—it will often highlight what's going on in potentially confusing situations like this.

Travis Brown
+1 for the citation and warning flag suggestion
pelotom
+2  A: 

You can only pattern match on literals and constructors. You can't match on variables. More here: http://www.haskell.org/tutorial/patterns.html

That being said, you may be interested in view patterns.

John