我可以派生一个新类型的“数据”实例吗?
Posted
技术标签:
【中文标题】我可以派生一个新类型的“数据”实例吗?【英文标题】:Can I derive a newtype instance of `Data`? 【发布时间】:2018-09-25 10:55:21 【问题描述】:我有一个新类型T
:
newtype T = T Text
我想为它派生Data
。所以-XGeneralizedNewtypeDeriving -XDerivingStrategies
我愿意
deriving newtype instance Data T
我希望这对于 GHC 来说是一个不费吹灰之力的推导,但我收到了一条令人讨厌的错误消息(附在下面)。该错误消息似乎来自coerce
的应用程序,由newtype
派生引起。
如果我正确理解角色注释,他们将需要在Data
的实例方法上进行类型声明,但没有。我需要滚动我自己的实例吗?
• Couldn't match representation of type ‘c1 Text’
with that of ‘c1 T’
arising from a use of ‘ghc-prim-0.5.2.0:GHC.Prim.coerce’
NB: We cannot know what roles the parameters to ‘c1’ have;
we must assume that the role is nominal
• In the expression:
ghc-prim-0.5.2.0:GHC.Prim.coerce
@(forall (c :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep
-> TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep).
forall (d :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep)
(b :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep).
Data d => c (d -> b) -> d -> c b
-> forall (g :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep).
g -> c g
-> Text -> c Text)
@(forall (c :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep
-> TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep).
forall (d :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep)
(b :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep).
Data d => c (d -> b) -> d -> c b
-> forall (g :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep).
g -> c g
-> T -> c T)
gfoldl
In an equation for ‘gfoldl’:
gfoldl
= ghc-prim-0.5.2.0:GHC.Prim.coerce
@(forall (c :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep
-> TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep).
forall (d :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep)
(b :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep).
Data d => c (d -> b) -> d -> c b
-> forall (g :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep).
g -> c g
-> Text -> c Text)
@(forall (c :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep
-> TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep).
forall (d :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep)
(b :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep).
Data d => c (d -> b) -> d -> c b
-> forall (g :: TYPE ghc-prim-0.5.2.0:GHC.Types.LiftedRep).
g -> c g
-> T -> c T)
gfoldl
When typechecking the code for ‘gfoldl’
in a derived instance for ‘Data T’:
To see the code I am typechecking, use -ddump-deriv
In the instance declaration for ‘Data T’
可能相关:GeneralizedNewtypeDeriving fails for PersistFieldSql
编辑:也许我应该只使用-XDeriveDataTypeable
?
【问题讨论】:
【参考方案1】:来自docs:
派生实例仅用于这些形式的声明 (任何类型同义词展开后):
newtype T v1..vn = MkT (t vk+1..vn) deriving (C t1..tj) newtype instance T s1..sk vk+1..vn = MkT (t vk+1..vn) deriving (C t1..tj)
在哪里 [...]
C 不是Read
、Show
、Typeable
或Data
。这些类不应该“查看”类型或其构造函数。你仍然可以推导出这些 新类型的类,但它以通常的方式发生,而不是通过这个 新机制。与Default deriving strategy协商。
重点是:newtype
的 Data
实例应该根据 newtype
本身的构造函数来制作,而不是根据底层类型的(几个)构造函数来制作。
一般化的newtype
实例只会将基础类型的Data
实例“强制”为newtype
,但这是错误的。
(不过,您收到的错误消息可能会更有帮助。)
结论:尝试改用DeriveDataTypeable
。这应该派生出正确的实例。
newtype T = T Text deriving (Data)
更准确地说,我们应该在适当的例子中看到这一点:
> data U = A | B deriving Data
> newtype T = T U deriving Data
> toConstr (T A)
T
相反,“新类型派生”实例将在此处生成 A
,公开底层构造函数,而不是预期的 T
。
【讨论】:
太好了,谢谢!你能扩展“不应该”的部分吗?我知道 Typeable 和朋友们确实想要创建类型的运行时表示。拥有一个简单地从派生对象继承此行为的实例(如果这是一个词)是完全非法的吗?我引入这种新类型(在我的具体情况下)的主要原因是通过区分T
和 Text
从语言中挤出更多的类型安全性。
涉及Generic
的案例是newtype Generically a = Generically a
,它是Generic
-behaviour 的包装器(将添加到GHC.Generics
)。它的所有实例都从 Generic
继承了它们的行为,通过 newtype 派生它为 Generic
做同样的事情并不是不合理的。这通过避免包装和展开来简化其他实例。这是一个哲学问题以上是关于我可以派生一个新类型的“数据”实例吗?的主要内容,如果未能解决你的问题,请参考以下文章