views:

357

answers:

3

I have a very simple question. I'd like to use a where clause after a bloc of code that uses bind operators but I get a compilation error.

Here is a simple example:

main =
    putStrLn "where clause test:" >>
    return [1..10] >>= \list ->
    print list'
        where list' = reverse list -- test1.hs:5:28: Not in scope: `list'

I can use a let clause for list' as in

main =
    putStrLn "where clause test:" >>
    return [1..10] >>= \list ->
    let list' = reverse list -- works of course
    in print list'

but I'd really like it if I could use a where clause...

I also tried with do notation

main = do
    putStrLn "where clause test:"
    list <- return [1..10]
    print list'
        where list' = reverse list --test3.hs:5:30: Not in scope: `list'

Same problem. Can I use a where clause in these circumstances?

+1  A: 

As far as I can tell, the where clause is only used in local bindings. The inner part of a >>(=) binding statement is not a local binding (two different kinds of bindings in that sentence).

Compare with this:

main = f [1..10]

f list =
    putStrLn "where clause test:" >> print list'
        where list' = reverse list

You might want to refer to the Haskell 98 syntax report - not sure how much help it would be.

If I'm wrong, someone will certainly correct me, but I'm pretty sure you can't use a where clause at all in the style you've shown above. list will never be in scope to a where clause unless it's a parameter to the function.

Mark Rushakoff
A lambda abstraction is an expression, not a declaration or binding, though it can bind new names...
ephemient
Whoa, who -1'ed this? I think it's correct, if not as complete as it could be.
ephemient
it's not relevant. the OP wants to use "list", a result from the middle of a bunch of monadic computations; not a value that is from outside the monad
newacct
+9  A: 
ephemient
why not just "let list' = reverse list"? in the last example?
newacct
Thanks, I should refer to the Haskell Report more often when I have fundamental questions about the language such as this.
the_edge
@newacct: OP's question already includes that variant, which obviously works.
ephemient
+6  A: 

As ephemient explains, you can't use where clauses the way you do.

The error happens because in this code:

main =
  return [1..10] >>= \list ->
  print list'
    where
      list' = reverse list

The where-clause is attached to the main function.

Here's that same function with more parentheses:

main = return [1..10] >>= (\list -> print list')
  where
    list' = reverse list

I think its fairly obvious why you get the "out of scope" error: The binding for list is deep inside the main expression, not something the where clause can reach.

What I usually do in this situation (and I've been bitten by the same thing a bunch of times). I simply introduce a function and pass the list as an argument.

main = do
  list <- return [1..10]
  let list' = f list
  print list'
  where
    f list = reverse list -- Consider renaming list,
                          -- or writing in point-free style

Of course, I imagine your actual code in the f function is a lot more that just reverse and that's why you want it inside a where clause, instead of an inline let binding. If the code inside the f function is very small, I'd just write it inside the let binding, and wouldn't go through the overhead of introducing a new function.

Tom Lokhorst
Thanks, your example with more parenthesis clears it up for me.
the_edge