tags:

views:

829

answers:

4

I am reading a tutorial that uses the following example (that I'll generalize somewhat):

f :: Foo -> (Int, Foo)
...
fList :: Foo -> [Int]
fList foo = x : fList bar
  where
    (x, bar) = f foo

My question lies in the fact that it seems you can refer to x and bar, by name, outside of the tuple where they are obtained. This would seem to act like destructuring parameter lists in other languages, if my guess is correct. (In other words, I didn't have to do the following:)

fList foo = (fst tuple) : fList (snd tuple)
      where
        tuple = f foo

Am I right about this behavior? I've never seen it mentioned yet in the tutorials/books I've been reading. Can someone point me to more info on the subject?

Edit: Can anything (lists, arrays, etc.) be destructured in a similar way, or can you only do this with tuples?

+2  A: 

Am I right about this behavior?

Yes. The names exist only in the block where you have defined them, though. In your case, this means the logical unit that your where clause is applied to, i.e. the expression inside fList.

Konrad Rudolph
A: 

Yes, you're right. Names bound in a where clause are visible to the full declaration preceding the where clause. In your case those names are f and bar.

(One of the hard things about learning Haskell is that it is not just permitted but common to use variables in the source code in locations that precede the locations where those variables are defined.)

The place to read more about where clauses is in the Haskell 98 Report or in one of the many fine tutorials to be found at haskell.org.

Norman Ramsey
+1  A: 

Another way to look at it is that code like this

x where x = 3

is roughly equivalent to

let x = 3 in x
+8  A: 

Seeing your edit, I think what your asking about is Pattern matching.

And to answer your question: Yes, anything you can construct, you can also 'deconstruct' using the constructors. For example, you're probably familiar with this form of pattern matching:

head :: [a] -> a
head (x:xs) = x
head []     = error "Can't take head of empty list"

However, there are more places where you can use pattern matching, other valid notations are:

head xs = case xs of
              (y:ys) -> y
              []     -> error "Can't take head of empty list"

head xs = let (y:ys) = xs
          in y

head xs = y
  where
    (y:ys) = xs

Note that the last two examples are a bit different from the first to because they give different error messages when you call them with an empty list.


Although these examples are specific to lists, you can do the same with other data types, like so:

first :: (a, b) -> a
first tuple = x
  where
    (x, y) = tuple

second :: (a, b) -> b
second tuple = let (x, y) = tuple
               in y

fromJust :: Maybe a -> a
fromJust ma = x
  where
    (Just x) = ma

Again, the last function will also crash if you call it with Nothing.

To sum up; if you can create something using constructors (like (:) and [] for lists, or (,) for tuples, or Nothing and Just for Maybe), you can use those same constructors to do pattern matching in a variety of ways.

Tom Lokhorst