Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unexpected pattern matching behavior

Tags:

haskell

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?

like image 216
user1604015 Avatar asked Dec 15 '22 06:12

user1604015


2 Answers

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
like image 77
bennofs Avatar answered Dec 31 '22 00:12

bennofs


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
like image 25
Lee Avatar answered Dec 31 '22 01:12

Lee