如何将类型类用于多态数据类型?

时间:2014-09-27 22:19:03

标签: haskell

我定义了以下数据类型:

type MySet a = (a -> Bool)

我想用它来定义一组具有以下属性的函数: 对于func3中的所有f,f 3 == 2.

func3 :: MySet (Eq a => a -> a)
func3 = (\x -> (x 3) == 2) 

出现以下错误:

Illegal polymorphic or qualified type: Eq a => a -> a
Perhaps you intended to use -XLiberalTypeSynonyms
In the type signature for `func3': func3 :: MySet (Eq a => a -> a)

如果我不使用Eq类型类,我会收到此错误:

No instance for (Eq a) arising from a use of `=='
Possible fix:
  add (Eq a) to the context of
  the type signature for func3 :: MySet (a -> a)

1 个答案:

答案 0 :(得分:5)

通常,解决涉及类型同义词(看起来像type ... = ...的问题)的问题的第一步是通过手动替换同义词(type表达式的左侧)来实现扩张(右手边)。所以:

func3 :: MySet (Eq a => a -> a)

变为:

func4 :: (Eq a => a -> a) -> Bool

现在GHC给了我们一个更有帮助的错误:

Illegal polymorphic or qualified type: Eq a => a -> a
Perhaps you intended to use RankNTypes or Rank2Types
In an expression type signature: (Eq a => a -> a) -> Bool

RankNTypes是一个有趣的扩展,但在这里不是很有用。 (我稍后会解释)。

早上好指出,您可能想要做的只是将约束移出括号:

func5 :: Eq a => (a -> a) -> Bool

然后GHC让您知道您还需要Num。除此之外,我们有一个看起来很普通的类型签名:=>之前的约束,然后是常规类型变量和特定类型。这很好;我们通常希望类型尽可能简单(但不是更多)。

但是让我们备份并检查两件事:输入同义词和RankNTypes

首先,您将MySet称为“数据类型”。想到它,这是一种危险的方式。它实际上是一个类型的同义词,这意味着它的含义:MySet (a -> a)(a -> a) -> Bool实际上是同义词;他们的意思是一样的。对比data MySetD a = MySetD (a -> Bool)之类的东西。在那里,MySetD实际上是一种诚实的上帝数据类型,不能被替换为wil-nilly,(a -> Bool),反之亦然。

第二:RankNTypes有什么用?如果我们打开该扩展程序,我们可以写下:

funcRN :: (forall a. (Eq a, Num a) => a -> a) -> Bool
funcRN = (\x -> (x 3) == 2) 

它可以用funcRN ((+) 1)之类的东西做你想做的事。但是这个呢?

g :: Int -> Int
g x = x - 1

funcRN g

这不起作用。 funcRN需要一个可以使用任何类型a的函数作为其参数,以便aEqNum的实例。这就是forall a. (Eq a, Num a) =>位意味着什么。由于g仅适用于一个类型Int,因此funcRN的类型签名是不可接受的。