假设我们有以下代码:
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
的内容也会有所帮助。
答案 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 ]) ]