如何手动定义Generic实例?

问题描述 投票:0回答:1

假设数据类型的 GHC.Generic 类的实例应由 GHC 通过派生机制自动生成,但它不适用于行类型和具有隐式参数的匿名记录。因此,我尝试手动定义泛型实例,并开始实验,使用 GHCi 复制常规数据类型的泛型实例来获取泛型表示,作为准备步骤,但即使在这里,我也面临来自类型检查器的无法解释的错误。

> import GHC.Generics
> data Point = Point { x :: Int, y :: Int } deriving (Show, Eq, Generic)
> from (Point 1 2)
M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = 1}} :*: M1 {unM1 = K1 {unK1 = 2}}}}
> :i Rep Point
type instance Rep Point
  = D1
      (MetaData
         "Point"
         "RowTypeDemo.ImplicitParamsAsAlternativeToRowType"
         "row-type-demo-0.0.1-inplace"
         False)
      (C1
         (MetaCons "Point" PrefixI True)
         (S1
            (MetaSel
               (Just "x") NoSourceUnpackedness NoSourceStrictness DecidedLazy)
            (Rec0 Int)
          :*: S1
                (MetaSel
                   (Just "y") NoSourceUnpackedness NoSourceStrictness DecidedLazy)
                (Rec0 Int)))

基于上述 GHCi 输出的 Point 通用实例:

data Point = Point { x :: Int, y :: Int } deriving (Show, Eq)
instance Generic Point where
  type Rep Point =
    D1
      (MetaData
         "Point"
         "RowTypeDemo.ImplicitParamsAsAlternativeToRowType"
         "row-type-demo-0.0.1-inplace"
         False)
      (C1
         (MetaCons "Point" PrefixI True)
         (S1
            (MetaSel
               (Just "x") NoSourceUnpackedness NoSourceStrictness DecidedLazy)
            (Rec0 Int)
          :*: S1
                (MetaSel
                   (Just "y") NoSourceUnpackedness NoSourceStrictness DecidedLazy)
                (Rec0 Int)))
  from :: Point -> Rep Point x
  from r = M1 {unM1 = L1 (M1 {unM1 = M1 {unM1 = K1 {unK1 = x r}} :*: M1 {unM1 = K1 {unK1 = y r}}})}

类型检查器失败并显示:

• Couldn't match type: M1
                         i0 c0 (M1 i1 c1 (K1 i2 Int) :*: M1 i3 c2 (K1 i4 Int))
                       :+: g0
                 with: M1
                         C
                         (MetaCons "Point" PrefixI True)
                         (S1
                            (MetaSel
                               (Just "x") NoSourceUnpackedness NoSourceStrictness DecidedLazy)
                            (Rec0 Int)
                          :*: S1
                                (MetaSel
                                   (Just "y")
                                   NoSourceUnpackedness
                                   NoSourceStrictness
                                   DecidedLazy)
                                (Rec0 Int))
  Expected: Rep Point x
    Actual: M1
              D
              (MetaData
                 "Point"
                 "RowTypeDemo.ImplicitParamsAsAlternativeToRowType"
                 "row-type-demo-0.0.1-inplace"
                 False)
              (M1 i0 c0 (M1 i1 c1 (K1 i2 Int) :*: M1 i3 c2 (K1 i4 Int)) :+: g0)
              x
• In the expression:
    M1
      {unM1 = L1
                (M1
                   {unM1 = M1 {unM1 = K1 {unK1 = x r}}
                             :*: M1 {unM1 = K1 {unK1 = y r}}})}
  In an equation for ‘from’:
      from r
        = M1
            {unM1 = L1
                      (M1
                         {unM1 = M1 {unM1 = K1 {unK1 = x r}}
                                   :*: M1 {unM1 = K1 {unK1 = y r}}})}
  In the instance declaration for ‘Generic Point’    | 87 |   from r = M1 {unM1 = L1 (M1 {unM1 = M1 {unM1 = K1 {unK1 = x r}} :*: M1 {unM1

= K1 {unK1 = y r}}})} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

我不明白问题出在哪里,因为 type family application

Rep Point x
应该等于它的值。

GHC版本是9.8.1

启用的扩展列表:

  DataKinds
  LambdaCase
  ImplicitParams
  OverloadedLabels
  OverloadedStrings
  TupleSections
  TypeFamilies
  UnicodeSyntax
  GADTs
  PolyKinds
  RankNTypes
  TypeOperators
  TypeApplications

更新:

感谢@leftaroundabout。 GHCi 生成错误的表示。

以下实现满足类型检查器的要求。

  from :: Point -> Rep Point x
  from r = M1 {unM1 = (M1 {unM1 = M1 {unM1 = K1 {unK1 = x r}} :*: M1 {unM1 = K1 {unK1 = y r}}})}

  to (M1 {unM1 = (M1 {unM1 = M1 {unM1 = K1 {unK1 = xx}} :*: M1 {unM1 = K1 {unK1 = yy}}})}) = Point xx yy
haskell generics ghc
1个回答
1
投票

您的定义中有零星的

L1
。这是
:+:
的构造函数,这对您的类型没有意义。

这个定义编译:

from r = M1 {unM1 = M1 {unM1 = M1 {unM1 = K1 {unK1 = x r}} :*: M1 {unM1 = K1 {unK1 = y r}}}}

或者,更易读的是,

from r = M1 . M1 $ M1 (K1 $ x r) :*: M1 (K1 $ y r)

不过,我相当怀疑写这样一个实例是否明智。一般来说,泛型实例的全部要点是它允许您定义类实例,而不需要编写处理类型结构的特殊代码。如果您无论如何都要编写这样的代码,为什么不立即实例化感兴趣的类呢?如果您的类型很不寻常,不支持派生的 Generic 实例,那么这可能更合适。

    

© www.soinside.com 2019 - 2024. All rights reserved.