假设我具有此数据类型:
data Zinf = NegInf | Z Integer | PosInf deriving (Eq, Ord, Show)
它也是Num
,因为定义了所有必需的操作。例如,当我键入PosInf + 1
时,解释将返回PosInf
。
现在,我导出另一种数据类型:
data Interval = Simple Integer | Complicated Zinf Zinf deriving (Eq, Show)
现在,由于Zinf也是Num
,因此我可以写下以下内容:
(Z 3) + 4
我得到
(Z 7)
我也可以测试
(Z 3) + 6 == 9
并且解释返回True
。
我也可以写Complicated 3 (6*7)
来获得Complicated (Z 3) (Z 42)
。
最后,我使用以下“间隔”数据类型创建树:
data BinTree a = Empty | Node a (BinTree a) (BinTree a) deriving (Eq, Show)
type ITree = BinTree Interval
现在,当我尝试编写此函数时:
moveBy :: ITree -> Integer -> ITree
moveBy Empty _ = Empty
moveBy (Node (Simple a) l r) i = (Node (Simple (a+i)) (moveBy l i) (moveBy r i))
moveBy (Node (Complicated a b) l r) i = (Node ( Complicated (a + i) (b + i) ) (moveBy l i) (moveBy r i))
它会产生以下错误:
* Couldn't match expected type `Zinf' with actual type `Integer'
* In the second argument of `(+)', namely `i'
In the second argument of `Complicated', namely `(b + i)'
In the first argument of `Node', namely `(Complicated (a + i) (b + i))'
为什么? b
的类型应为Zinf
,b+i
的类型应再次为Zinf
,因为i
是Integer
。 Complicated
恰好期望这种说法。
答案 0 :(得分:8)
现在,由于Zinf也是Num,我可以写下以下内容:
(Z 3) + 4
是的,但很重要的一点是,(+)
在这种情况下不是的类型为Zinf -> Integer -> Zinf
,看起来很像。您可能会认为4
的类型为integer,但实际上并非如此:它实际上的类型为Zinf
。这是令人惊讶的,因为3
确实具有整数类型!
发生了什么:Haskell数字文字是多态的。
Prelude> :t 1
1 :: Num p => p
因此,数字文字可以具有Integer
或Float
或实际上是Zinf
的类型,具体取决于上下文的要求。在(Z 3) + 4
中,这是Zinf
的{{1}},因此4
的类型为4
。太好了!
但是,这在Zinf
中不起作用,因为在那里Simple (a+i)
的类型由签名/上下文固定为i
。因此,您不能将其添加到Integer
。
但是,您可以做什么:可以通过将任何Zinf
值包装在Integer
中来将其“升级”为多态数(例如数字文字)。所以,你想在那里
fromInteger
或等效的moveBy (...) i = (Node (Simple (a + fromInteger i)) (...) (...)
。
† 实际上,数字文字的含义是它们也只是将{plain“整数文字包装在Simple (a + Z i)
中,以使其具有多态性。 < / p>
答案 1 :(得分:4)
写作时
Z 3 + 4
这并不意味着
Z 3 + (4 :: Integer)
但是
Z 3 + (4 :: Zinf)
并使用fromInteger :: Integer -> Zinf
(由您在Num
实例中实现)将4
解释为Zinf
。实际上,加法必须始终在相同类型的两个值之间(例如,两个Zinf
或两个Integer
之间),决不能在Zinf
和Integer
之间
您可以修改moveBy
以接受Zinf
参数,这样加法就可以了:
moveBy :: ITree -> Zinf -> ITree
-- implementation as in the question
或者您可以像编写Integer
时隐式执行的一样,将分配给moveBy
的{{1}}转换为Zinf
:
Z 3 + 4