类型类实例检查无法检测“分段实例”

时间:2015-07-17 07:54:20

标签: haskell compiler-errors type-inference typeclass type-families

我有一系列由类型级整数索引的数据类型,并且我以“分段”方式将它们定义为某种类型类的实例,这在尝试派生另一个类的实例时会导致问题。为了说明,我已将问题隔离如下。请考虑以下代码:

{-# 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包含所有整数ZeroOneTwo等等。

但是,在添加以下行并重新编译之后,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的唯一实例是ZeroSucc Zero并且(Nat n0) => Succ (Succ n0)Succ n都不匹配。当我用原始

替换MODIFY块时,成功编译支持这种猜测
instance (Nat n) => Nat (Succ n) where
  toInt _ = 1 + toInt (undefined :: n)

即使使用修改过的块,如何使likeInttoInt一样工作?这对我的实际项目非常重要。

2 个答案:

答案 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

(这确实可以编译)。