
On 11/20/06, Ravi Nanavati
While playing with some pattern guard code, I discovered some scoping behavior that I wasn't expecting. I learned that pattern guard bindings aren't visible in their equation's where clauses. To make this concrete, in the following (contrived) example, "test1" works, but "test2" complains that 'v' is unbound:
data Bar = Baz Int | Biz Int | Empty
toInt (Baz i) = Just i toInt (Biz i) = Just (2 * i) toInt Empty = Nothing
test1 :: Bar -> Int test1 b | Just v <- toInt b = v + 1 test1 _ = 0
test2 :: Bar -> Int test2 b | Just v <- toInt b = k where k = v + 1 test2 _ = 0
From an implementation perspective, I understand the behavior as an artifact of desugaring pattern guards into a case expression. But, as a user, it seems like an odd restriction on how I organize my code (since I often prefer to separate out tricky bits of logic into nearby where clauses without disturbing the main flow of the right-hand side).
Moreover, unless I'm missing something, a slight tweak to the desugaring (by adding irrefutable patterns corresponding to the pattern guard bindings) could easily recover the behavior I expected.
I'm thinking something like this (which could be tweaked to remove the repeated expression, of course):
test2' :: Bar -> Int test2' b | Just v <- toInt b = k where Just v = toInt b k = v + 1
Anyway, what do other people think? Do people besides me find it pleasing to have pattern guard bindings visible in where clauses? Or are people more worried about the failures that would happen if you used a binding from a pattern guard that ended up false?
What about this case:
test2 :: Bar -> Int test2 b | Just v <- toInt b = k | v <- () = k | otherwise = k where k = v + 1 test2 _ = 0
Making bindings available outside their scope is just plain wrong (and obviously so), imo. Perhaps you're looking for a let-expression instead of a where-clause? -- Cheers, Lemmih