具有类型约束的Haskell类型族实例

时间:2015-10-07 02:51:50

标签: haskell typeclass type-constraints type-families type-level-computation

我试图用类型族来表示表达式,但我似乎无法弄清楚如何编写我想要的约束,而我开始觉得这是不可能的。这是我的代码:

class Evaluable c where
    type Return c :: *
    evaluate :: c -> Return c

data Negate n = Negate n

instance (Evaluable n, Return n ~ Int) => Evaluable (Negate n) where
    type Return (Negate n) = Return n
    evaluate (Negate n) = negate (evaluate n)

这一切都很好,但它并不能完全表达我想要的东西。在Negate Evaluable实例的约束中,我说Negate中的表达式的返回类型必须是Int(带Return n ~ Int)所以我可以称之为否定,但这太限制了。返回类型实际上只需要是具有Num函数的negate类型类的实例。这样Double s,IntegerNum的任何其他实例也可以被否定,而不仅仅是Int。但我不能只写

Return n ~ Num

因为Num是一个类型类,而Return n是一个类型。我也不能把

Num (Return n)

因为Return n是一个类型而不是类型变量。

我正在尝试用Haskell做什么?如果不是,它应该是,还是我误解了背后的一些理论?我觉得Java可以添加这样的约束。如果这个问题更清楚,请告诉我。

编辑:谢谢大家,回复正在帮助并且正在达到我的怀疑。如果没有UndecidableInstances,类型检查器似乎无法处理我想要做的事情,所以我的问题是,我想表达的是真正无法判断的吗?它是Haskell编译器,但它是否一般?也就是说甚至存在约束,这意味着“检查返回n是Num的实例”,这可以判断为更高级的类型检查器吗?

2 个答案:

答案 0 :(得分:6)

实际上,你可以完全按照你提到的那样做:

{-# LANGUAGE TypeFamilies, FlexibleContexts, UndecidableInstances #-}

class Evaluable c where
    type Return c :: *
    evaluate :: c -> Return c

data Negate n = Negate n

instance (Evaluable n, Num (Return n)) => Evaluable (Negate n) where
    type Return (Negate n) = Return n
    evaluate (Negate n) = negate (evaluate n)

Return n当然是一个类型,它可以像Int一样可以是类的实例。你的困惑可能是关于约束的论证。答案是“任何正确的东西”。 Int的种类为*Return n的种类也是Num* -> Constraint*种类,因此Num Int的任何都可以成为其参数。写Num (a :: *)作为约束完全合法(尽管是空洞的),就像{{1}}合法一样。

答案 1 :(得分:2)

为了补充Eric的答案,让我提出一个可能的选择:使用函数依赖而不是类型族:

class EvaluableFD r c | c -> r where
  evaluate :: c -> r

data Negate n = Negate n

instance (EvaluableFD r n, Num r) => EvaluableFD r (Negate n) where
  evaluate (Negate n) = negate (evaluate n)

这让我更容易谈论结果类型。例如,你可以写

foo :: EvaluableFD Int a => Negate a -> Int
foo x = evaluate x + 12

你也可以使用ConstraintKinds部分地应用它(这就是为什么我把参数放在那个看起来很滑稽的顺序中):

type GivesInt = EvaluableFD Int

你也可以和你的班级一起做这件事,但这会更烦人:

type GivesInt x = (Evaluable x, Result x ~ Int)