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.