Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

The problem is that let-in is an expression, which can be used inside other expressions, while where can only be used on a (module|class|instance|GADT|...) declaration or a (function|pattern) binding.

From the Haskell 98 report on declarations and bindings,

p | g1 = e1
    | g2 = e2
    …
    | gm = em
  where { decls }

is sugar for

p = let decls in
      if g1 then e1 else
      if g2 then e2 else
      …
      if gm then em else error "Unmatched pattern"

or, simplifying things by removing guards,

p = e where { decls }

is sugar for

p = let decls in e

in both function and pattern bindings. This is true even when your e is a do {} construct.

If you want to have a binding local to a particular subexpression within a larger expression, you need to use let-in (or simply let inside a do, but that's just sugar for let-in).

You can't even write

main = do
    putStrLn "where clause test: "
    list <- return [1..10]
    (print list' where list' = reverse list)

because "e where { decls }" is not a legal expression – where can only be used in declarations and bindings.

main = do
    putStrLn "where clause test: "
    list <- return [1..10]
    let list' = list'' where list'' = reverse list
    print list'

This is legal (if somewhat contrived).

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top