我有一系列由类型级整数索引的数据类型,并且我以“分段”方式将它们定义为某种类型类的实例,这在尝试派生另一个类的实例时会导致问题。为了说明,我已将问题隔离如下。请考虑以下代码:
{-# LANGUAGE ScopedTypeVariables, TypeSynonymInstances,
FlexibleInstances , UndecidableInstances #-}
data Zero
data Succ n
type One = Succ Zero
type Two = Succ One
type Three = Succ Two
class Nat n where
toInt :: n -> Int
instance Nat Zero where
toInt _ = 0
instance Nat One where ------------------------- START MODIFY
toInt _ = 1
instance (Nat n) => Nat (Succ (Succ n)) where
toInt _ = 2 + toInt (undefined :: n) --------- END MODIFY
这是"Fun with type functions" by Kiselyov, Jones and Shan中定义的类型级整数的略微修改。这编译得很好,toInt
似乎按预期工作。现在Nat
包含所有整数Zero
,One
,Two
等等。
但是,在添加以下行并重新编译之后,GHC抱怨:
class LikeInt n where
likeInt :: n -> Int
instance (Nat n) => LikeInt (Succ n) where
likeInt = toInt
错误:无法从上下文(Nat (Succ n))
中推断出因使用“toInt
”而导致的(Nat n)
。
我的猜测是,当GHC推断toInt
的参数类型为Succ n
时,Nat
的唯一实例是Zero
,Succ Zero
并且(Nat n0) => Succ (Succ n0)
和Succ n
都不匹配。当我用原始
MODIFY
块时,成功编译支持这种猜测
instance (Nat n) => Nat (Succ n) where
toInt _ = 1 + toInt (undefined :: n)
即使使用修改过的块,如何使likeInt
像toInt
一样工作?这对我的实际项目非常重要。
答案 0 :(得分:0)
你不能定义这个实例吗?
instance Nat n => LikeInt n where
likeInt = toInt
*Main> likeInt (undefined :: Zero)
0
*Main> likeInt (undefined :: One)
1
*Main> likeInt (undefined :: Two)
2
*Main> likeInt (undefined :: Three)
3
或者您想避免Nat
约束?
答案 1 :(得分:0)
编译器告诉你该怎么做。
你有
instance (Nat n) => LikeInt (Succ n) where
likeInt = toInt
编译说:
Could not deduce (Nat (Succ n)) arising from a use of ‘toInt’ from the context (Nat n)
因此我们更改上下文以给出编译器要求的约束。魔术!
instance (Nat (Succ n)) => LikeInt (Succ n) where
likeInt = toInt
(这确实可以编译)。