我可以派生一个新类型的“数据”实例吗?

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 不是ReadShowTypeableData。这些类不应该“查看”类型或其构造函数。你仍然可以推导出这些 新类型的类,但它以通常的方式发生,而不是通过这个 新机制。与Default deriving strategy协商。

重点是:newtypeData 实例应该根据 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 和朋友们确实想要创建类型的运行时表示。拥有一个简单地从派生对象继承此行为的实例(如果这是一个词)是完全非法的吗?我引入这种新类型(在我的具体情况下)的主要原因是通过区分 TText 从语言中挤出更多的类型安全性。 涉及Generic 的案例是newtype Generically a = Generically a,它是Generic-behaviour 的包装器(将添加到GHC.Generics)。它的所有实例都从 Generic 继承了它们的行为,通过 newtype 派生它为 Generic 做同样的事情并不是不合理的。这通过避免包装和展开来简化其他实例。这是一个哲学问题

以上是关于我可以派生一个新类型的“数据”实例吗?的主要内容,如果未能解决你的问题,请参考以下文章

我可以在 julia 中定义一个类似于原始数据类型的新类型吗?

为更高种类的数据派生实例

python3 之 类

你真的了解扩展方法吗?

使用多态性是在一个容器下存储不同数据类型的好方法吗?

如果继承类型受到保护,我可以使基类的指针指向派生对象吗? [复制]