繁体   English   中英

GHC Generics中的Rep类型

[英]Rep type in GHC Generics

我正在尝试构建一个自动知道如何创建默认值的Default类。 所以我阅读了相关的维基页面 ,我的问题归结为:为什么这个类型检查:

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DefaultSignatures #-}

import GHC.Generics

-- From https://wiki.haskell.org/GHC.Generics (sort of)
class GSerialize f where
  gput :: f a -> [Int]
class Serialize a where
  put :: a -> [Int]
  default put :: (Generic a, GSerialize (Rep a)) => a -> [Int]
  put a = gput (from a)

但事实并非如此

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DefaultSignatures #-}

import GHC.Generics

class GDefault a where
  gdef :: a
class Default a where
  def :: a
  default def :: (Generic a, GDefault (Rep a)) => a
  def = gdef . from

错误是:

• Expecting one more argument to ‘Rep a’
  Expected a type, but ‘Rep a’ has kind ‘* -> *’
• In the first argument of ‘GDefault’, namely ‘Rep a’
  In the type signature:
    def :: (Generic a, GDefault (Rep a)) => a
  In the class declaration for ‘Default’

这里的编译器错误是有用的,但只是以令人讨厌的方式告诉你究竟出了什么问题而不是为什么它是错的。

预期类型但“Rep a”有种类“* - > *”。

所以这里的问题是Rep (一个类型族)需要两个参数(称为ap ,如在Rep ap ); 它作为类型级函数将这两个类型参数映射到“通用”类型。 例如,

data Empty deriving Generic

instance Generic Empty where
  type Rep Empty =
    D1 ('MetaData "Empty" "Main" "package-name" 'False) V1

-- taken from https://hackage.haskell.org/package/base-4.9.0.0/docs/GHC-Generics.htm
  • a ,例如Empty ,表示我们通用的类型。

  • p是一个虚拟类型,因此我们可以为更高级别的类型重用我们的表示类型( 参见Generic1中的Generic1 )。

因此,在上面的例子中, Rep Empty p将简化为D1 ('MetaData ...) V1 p

我们通常可以忽略p除非在定义利用泛型的新类型类时。 我们希望对类型如D1 ('MetaData ...) V1 p进行模式匹配,但我们需要一些处理额外参数的方法。

然后一个技巧是将D1 ('MetaData ...) V1视为更高级别的类型(如仿函数)。 这是我们fGDefault

class GDefault f where
  gdef :: f a

是的a将永远是我们永远不会使用的这个愚蠢的参数,但作为对线噪声的回报,我们能够在我们的实例中对f进行模式匹配。 这里有四个实例允许产品类型的自动泛型def实现( :*:是一个提升的元组):

instance GDefault U1 where
  gdef = U1

instance Default a => GDefault (K1 i a) where
  gdef = K1 def

instance (GDefault a, GDefault b) => GDefault (a :*: b) where
  gdef = gdef :*: gdef

instance GDefault a => GDefault (M1 i c a) where
  gdef = M1 gdef

这个,以及数字塔的一些明智的默认值,将让我们定义数据类型,如data Foo = Foo Int Char Float deriving (Show, Generic)和评估show (def :: Foo)"Foo 0 0 0.0"

你的代码有gdef :: a ,这是错误的。 我们想要gdef :: fa因为类型类是在类型* -> *的类型上定义的,因此是错误消息。

为了利用这个助手类,我们做了很多事情:

class Default a where
  def :: a

  default def :: (Generic a, GDefault (Rep a)) => a
  def = to gdef

to :: Rep ax -> a引入了一个虚假的x ,它与我们的gdef :: fa结合生成f ~ Rep a ,丢弃x并且正是我们想要的。

您可以在data-default包中看到这种方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM