我正在尝试在以下代码中对Data.Typeable.TypeRep进行模式匹配:
import Data.Typeable (TypeRep, typeOf)
tyI = typeOf (1 :: Int)
tyD = typeOf (3.14 :: Double)
tyB = typeOf (True :: Bool)
func1 :: TypeRep -> Bool
func1 tyI = False
func1 tyD = False
func1 _ = True
func2 :: TypeRep -> Bool
func2 tr = case tr of
tyI -> False
tyD -> False
_ -> True
func3 :: TypeRep -> Bool
func3 tr = if tr == tyI then False else
if tr == tyD then False else
True
...我在编译时收到这些警告:
[1 of 1] Compiling Main ( /home/[..]/test.hs, interpreted )
/home/[..]/test.hs:8:1: Warning:
Pattern match(es) are overlapped
In an equation for ‘func1’:
func1 tyD = ...
func1 _ = ...
/home/[..]/test.hs:23:12: Warning:
Pattern match(es) are overlapped
In a case alternative:
tyD -> ...
_ -> ...
Ok, modules loaded: Main.
另外,我得到了令人惊讶的结果:
*Main> func1 tyI
False
*Main> func1 tyD
False
*Main> func1 tyB
False -- !!!
*Main> func2 tyI
False
*Main> func2 tyD
False
*Main> func2 tyB
False -- !!!
*Main> func3 tyI
False
*Main> func3 tyD
False
*Main> func3 tyB
True -- Ok!
所以只有最后一个函数func3似乎产生了预期的结果。如何使用模式匹配(func1)和case(func2)函数在TypeReps上正常工作?
答案 0 :(得分:3)
问题是在模式匹配中tyI
和tyD
被视为普通变量(而不是与同名的全局常量进行比较)。以下代码完全等同于您的代码。
func1 :: TypeRep -> Bool
func1 x = False
func1 y = False
func1 _ = True
func2 :: TypeRep -> Bool
func2 tr = case tr of
x -> False
y -> False
_ -> True
正如您在func3
中观察到的那样,解决此问题的方法是明确比较。您也可以使用警卫:
func4 :: TypeRep -> Bool
func4 tr | tr == tyI = False
| tr == tyD = False
| otherwise = True
通常,构造函数中可以匹配的equals左侧允许的唯一内容是构造函数。一个例外是数字文字,您会注意到在模式中使用这些文字会对整个函数产生Eq
约束
even :: (Num a, Eq a) => a -> Bool
even 0 = True
even 1 = False
even n = even (n-2)
如果添加OverloadedLists
和OverloadedStrings
,则会添加此规则的类似例外情况。该规则几乎适用于所有情况。
答案 1 :(得分:3)
此处tyI
:
func1 :: TypeRep -> Bool
func1 tyI = False
与此处定义的tyI
无关:
tyI = typeOf (1 :: Int)
事实上,您对func1
的定义可以写成:
func1 :: TypeRep -> Bool
func1 x = False
同样适用于func2
- 变量tyI
,tyB
和tyD
只有模式变量名称,并且不引用您的全局绑定。
但是,您在tyI
中对tyD
和func3
的使用确实引用了全局定义,因此func3 tyB
返回True的原因。