数据类型在函数参数内不起作用

时间:2019-11-25 22:50:03

标签: haskell

假设我具有此数据类型:

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的类型应为Zinfb+i的类型应再次为Zinf,因为iIntegerComplicated恰好期望这种说法。

2 个答案:

答案 0 :(得分:8)

  

现在,由于Zinf也是Num,我可以写下以下内容:

(Z 3) + 4

是的,但很重要的一点是,(+)在这种情况下不是的类型为Zinf -> Integer -> Zinf,看起来很像。您可能会认为4的类型为integer,但实际上并非如此:它实际上的类型为Zinf。这是令人惊讶的,因为3 确实具有整数类型!

发生了什么:Haskell数字文字是多态的。

Prelude> :t 1
1 :: Num p => p

因此,数字文字可以具有IntegerFloat或实际上是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之间),决不能在ZinfInteger之间

您可以修改moveBy以接受Zinf参数,这样加法就可以了:

moveBy :: ITree -> Zinf -> ITree
-- implementation as in the question

或者您可以像编写Integer时隐式执行的一样,将分配给moveBy的{​​{1}}转换为Zinf

Z 3 + 4