Haskell:类型族的实例定义

时间:2012-05-08 01:55:16

标签: haskell type-families

假设我们有以下代码:

class C t where
  g :: t

instance C Int where
  g = 42

简单。我们也可以在Int上定义函数,如下所示:

f1 :: Int -> Int
f1 x = x * x

我一直在使用类型系列,特别是因为Data.Has使用它们,我想将它们插入IxSet

但在这里,我将提出一个简化的例子。假设我们想要定义一个类似于Int的新类型X。我们可以这样做:

type family X
type instance X = Int

然后我们可以在X上定义函数,如下所示:

f2 :: X -> X
f2 x = x * x + 1

到目前为止没有问题。现在让我们尝试定义一个实例C X,就像我们为C Int所做的那样:

instance C X where
  g = 43

哦,现在我们有以下错误:

  

实例中的非法类型同义词系列应用程序:X
  在'C X'

的实例声明中

现在让我们尝试一些不同的东西:

newtype NewX = NewX X

instance C NewX where
  g = 43

现在我们又遇到了另一个错误,即:

  

(Num NewX)没有实例     来自文字'43'

似乎newtype关键字消除了有关上一类属于哪些类的任何信息。但是,我似乎无法避免newtype,因为我无法在实例定义中使用类型系列。

有没有更好的方法来做到这一点,而不必使用其他显式实例提及重写实例定义,否则将被推断?


背景信息:

我需要这个工作的原因如下:

import Data.Has
import Data.IxSet

data Col1 = Col1; type instance TypeOf Col1 = Text
data Col2 = Col2; type instance TypeOf Col2 = Text

type Row = FieldOf Col1 :&: FieldOf Col2;

instance Indexable Row where
  empty = ixSet [ixFun $ (\x -> [ Col1 ^. x ]) ] -- Maybe add some more indexes later

这失败了:

  

实例中的非法类型同义词系列应用程序:Row
  在'Indexable Row'

的实例声明中

使Row成为newtype会导致以下错误:

  

没有实例(包含(Labeled Col1文本)行)     因使用“^”而引起的   可能的修复:     为...添加实例声明     (包含(Labeled Col1 Text)Row)

我可以解决这个问题的唯一方法是添加一个long derived子句,如下所示:

newtype Row = Row (FieldOf Col1 :&: FieldOf Col2)
  deriving 
  (
    Contains (Labelled Col1 Text), -- Add this for every column
    Contains (Labelled Col2 Text)  -- ...
  )

即使允许我“typedef”Contains (Labelled x (TypeOf x))HasCol x的内容也会有所帮助。

2 个答案:

答案 0 :(得分:5)

以下文件在此处编译:

{-# LANGUAGE GeneralizedNewtypeDeriving, TypeFamilies #-}

class C a where g :: a
type family X
type instance X = Int
newtype NewX = NewX X deriving Num
instance C NewX where g = 43

答案 1 :(得分:3)

newtype就是这样 - 它定义了一种新类型,而type定义了一个同义词。如果你不喜欢一堆派生子句,那么总是可以使用与底层类型的同构

instance C NewX where
   g = NewX  43

类型同义词与Instance声明不兼容的原因是函数(包括类型函数)只能在一个方向上工作。您只能在构造函数上进行模式匹配,因此newtype允许您以零运行时成本引入新类型构造函数。在您的问题中,为什么不

newtype Row = Row {runRow :: FieldOf Col1 :&: FieldOf Col2}

instance Indexable Row where
  empty = ixSet [ixFun $ (\x -> [ Col1 ^. (runRow x) ]) ]

我应该注意,一般来说GeneralizedNewtypeDeriving是不健全的。并不意味着你应该避免使用它,但暗示你想要的东西可能是不可能的。


编辑(提问者):

更好的是,甚至不需要更改数据类型行

newtype Row = Row ( FieldOf Col1 :&: FieldOf Col2 )

instance Indexable Row where
  empty = ixSet [ixFun $ (\(Row x) -> [ Col1 ^. x ]) ]