简体   繁体   中英

Pattern matching in case expressions/list comprehensions

Why does the following attempt to pattern-match in a list comprehension not work?

Example: simultaneous substitution of atoms in a term data type.

The data type:

data Term a 
    = Atom a
    | Compound (Term a) (Term a)
    deriving Show

The substitution algorithm for atoms in terms (picks the first matching substitution if any and ignores the rest):

subs :: [(Term a, Term a)] -> Term a -> Term a
subs subList term = case term of 
    atom@(Atom x)       ->  let substitutions = 
                                [ s | s@(Atom x, _) <- subList ]
                            in  if null substitutions
                                then atom
                                else snd . head $ substitutions
    (Compound t1 t2)    -> Compound (subs subList t1) (subs subList t2)

Some test data:

subList = [((Atom 'a'), Compound (Atom 'b') (Atom 'c'))]
term1 = Atom 'a' 
term2 = Atom 'x' 

Running the example results in:

>: subs subList term1
Compound (Atom 'b') (Atom 'c')

which is the desired behaviour, and

>: subs subList term2
Compound (Atom 'b') (Atom 'c')

which is not.

Strangley explicit matching works:

subs'' :: [(Term Char, Term Char)] -> Term Char -> Term Char
subs'' subList term = case term of 
    atom@(Atom _)       ->  let substitutions = 
                            [ s | s@(Atom 'a', _) <- subList ]
                            in  if null substitutions
                                then atom
                                else snd . head $ substitutions
    (Compound t1 t2)    -> Compound (subs subList t1) (subs subList t2)

subs''' subList term = case term of 
     atom@(Atom _)       ->  let substitutions = 
                             [ s | s@(Atom 'x', _) <- subList ]
                             in  if null substitutions
                                 then atom
                                 else snd . head $ substitutions
     (Compound t1 t2)    -> Compound (subs subList t1) (subs subList t2)

feed with the test data result in:

>: subs'' subList term1 or >: subs'' subList term2
Compound (Atom 'b') (Atom 'c')

>: subs''' subList term1 or >: subs''' subList term2
Atom 'x'

What am I missing?

Haskell has linear patterns, meaning that there must be no repeated variables in patterns. Also, pattern variables in inner expressions shadow outer variables, instead of establishing equality of identical variables.

You're trying to do something like this:

charEq :: Char -> Char -> Bool
charEq c c = True
charEq _ _ = False

But this is an error because of the repeated variables. If we move the second c to an inner expression, it compiles, but it still doesn't work as intended:

charEq :: Char -> Char -> Bool
charEq c d = case d of
  c -> True
  _ -> False

Here the inner c is just a new variable that shadows the outer c , so charEq always returns True .

If we'd like to check for equality we must use == explicitly:

subs :: [(Term a, Term a)] -> Term a -> Term a
subs subList term = case term of 
    atom@(Atom x)       ->  let substitutions = 
                                [ s | s@(Atom x', _) <- subList, x == x' ]
                            in  if null substitutions
                                then atom
                                else snd . head $ substitutions
    (Compound t1 t2)    -> Compound (subs subList t1) (subs subList t2)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM