4

Here's (a simplified version of) my code:

data Exp = Var String

test :: Exp -> String -> Bool
test e vIn = case e of
                  Var vIn -> True
                  _       -> False

When I run this:

test (Var "X") "Y"

I get True, which is odd because it requires matching (Var vIn) and (Var s), s ~= vIn.

Can anyone explain what's going on, and suggest a way to fix it?

user1604015
  • 415
  • 2
  • 9
  • The two appearances of `vIn` in your code bind separately, so the first matching binds `vIn` to `"X"` and the second binds `vIn` to `"Y"` (and shadows the original). – Fixnum Sep 14 '13 at 19:06
  • That's what I figured... but is there any way to get the behavior that I wanted originally? (e.g. by forcing them to bind to the same thing?) – user1604015 Sep 14 '13 at 19:08
  • See also [Pattern matching identical values](https://stackoverflow.com/q/1179008/791604). (Though not quite a dupe, since this question has the identical patterns in separate case statements, and the discussion of shadowing below is well worth keeping in its own right.) – Daniel Wagner Jun 23 '17 at 18:01

2 Answers2

8

Haskell doesn't allow to match variables in patterns, because that would require the types of these variables to be an instance of Eq. For example, this doesn't work

isEqual :: Int -> Int -> Bool
isEqual a a = True
isEqual _ _ = False

It gives the error:

Conflicting definitions for `a'
...
In an equation for `isEqual

If Haskell doesn't allow such things, why does your example compile then? What happens in your code is that the vIn variable in the case statement shadows the vIn variable bound in the equation for test. The compiler also warns you about this if you compile with the -Wall flag:

code.hs:7:18: Warning:
This binding for `vIn' shadows the existing binding
  bound at code.hs:6:8

This means that there are two vIn variables, which are not equal, Only the inner one is visible, because it shadows the outer one.

To fix the code, you'll have to explictly compare the function argument to the vIn value matched in the case:

data Exp = Var String

test :: Exp -> String -> Bool
test e x = case e of
         Var vIn -> vIn == x -- Explicitly compare vIn to x
         _       -> False

Or just use guards and pattern match on Var in the test equation if that's an option:

data Exp = Var String

test :: Exp -> String -> Bool
test (Var a) vIn
  | a == vIn = ... {- Code for the case that vIn == a -}
  | otherwise = False
bennofs
  • 11,873
  • 1
  • 38
  • 62
  • Should that be (Var a) rather than (Test a) in the last block? – user1604015 Sep 14 '13 at 19:15
  • 1
    You might want to add that the reason you can't pattern match on variables in Haskell is that they didn't want to introduce an Eq requirement in pattern matches. – kqr Sep 14 '13 at 21:00
4

The vIn in your match is shadowing the vIn function argument, and the binding always succeeds. You can bind to a new variable and use a pattern guard to check the values are equal:

test e vIn = case e of
                  Var v | v == vIn -> True
                  _       -> False

alternatively you can match against Var directly instead of using case:

test (Var v) vIn = v == vInt
Lee
  • 142,018
  • 20
  • 234
  • 287
  • Sure. I was hoping for a way to for a way to do it without nested matching. But I guess this isn't possible? – user1604015 Sep 14 '13 at 19:10
  • 1
    @user1604015 - You can match directly against the `Exp` argument on the left-hand side of your function definition, see update. – Lee Sep 14 '13 at 19:15