2

假设我们有以下代码:

class C t where
  g :: t

instance C Int where
  g = 42

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

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

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

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

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

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

Illegal type synonym family application in instance: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

这失败了:

Illegal type synonym family application in instance:Row
在实例声明中为'Indexable Row'

制作Rowanewtype会导致以下错误:

(Contains (Labeled Col1 Text) Row) 没有因使用 `^.' 而产生的实例 可能的解决方法:为 (Contains (Labeled Col1 Text) Row) 添加一个实例声明

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

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也会有所帮助。

4

2 回答 2

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
于 2012-05-08T02:06:28.713 回答
3

Anewtype就是这样做的——它定义了一个新类型,而 atype定义了一个同义词。如果您不喜欢一堆派生子句,则始终可以将同构与基础类型一起使用

instance C NewX where
   g = NewX  43

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

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

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

我应该指出,总的来说GeneralizedNewtypeDeriving是不健全的。并不意味着您应该避免使用它,但确实暗示您想要的可能是不可能的。


编辑(提问者):

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

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

instance Indexable Row where
  empty = ixSet [ixFun $ (\(Row x) -> [ Col1 ^. x ]) ]
于 2012-05-08T03:37:57.890 回答