GHC 泛型中的代表类型
Rep type in GHC Generics
我正在尝试构建一个自动知道如何创建默认值的 Default
class。所以我读了relevan wiki page,我的问题归结为:为什么这个类型检查:
{-# 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’
这里的编译器错误很有用,但只是以那种烦人的方式告诉你到底哪里出了问题,而不是为什么它是错的。
Expected a type but "Rep a" has kind "* -> *".
所以这里的问题是Rep
(一个类型族)需要两个参数(称它们为a
和p
,如Rep a p
);它作为类型级函数将这两个类型参数映射到 "generic" 类型。例如,
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
是一个虚拟类型,因此我们可以将我们的表示类型重用于更高级别的类型 (see Generic1
in the documentation)。
因此,在上面的示例中,Rep Empty p
将简化为 D1 ('MetaData ...) V1 p
。
我们通常可以忽略 p
,除非涉及定义利用泛型的新类型classes。我们希望 对 D1 ('MetaData ...) V1 p
等类型进行模式匹配,但我们需要某种方式来处理额外参数。
一个技巧是将 D1 ('MetaData ...) V1
视为更高级别的类型(如函子)。这是我们在 GDefault
.
中的 f
class GDefault f where
gdef :: f a
是的,a
将永远是我们永远不会使用的这个愚蠢的参数,但是在 return 中,对于线路噪声,我们能够在我们的实例中对 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 :: f a
,因为类型 class 是在类型 * -> *
上定义的,因此会出现错误消息。
为了利用这个帮手 class,我们做了很多和您一样的事情:
class Default a where
def :: a
default def :: (Generic a, GDefault (Rep a)) => a
def = to gdef
to :: Rep a x -> a
引入了一个虚假的 x
,它与我们的 gdef :: f a
结合生成 f ~ Rep a
,丢弃了 x
并且正是我们想要的.
You can see this approach elaborated in the data-default
package.
我正在尝试构建一个自动知道如何创建默认值的 Default
class。所以我读了relevan wiki page,我的问题归结为:为什么这个类型检查:
{-# 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’
这里的编译器错误很有用,但只是以那种烦人的方式告诉你到底哪里出了问题,而不是为什么它是错的。
Expected a type but "Rep a" has kind "* -> *".
所以这里的问题是Rep
(一个类型族)需要两个参数(称它们为a
和p
,如Rep a p
);它作为类型级函数将这两个类型参数映射到 "generic" 类型。例如,
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
是一个虚拟类型,因此我们可以将我们的表示类型重用于更高级别的类型 (seeGeneric1
in the documentation)。
因此,在上面的示例中,Rep Empty p
将简化为 D1 ('MetaData ...) V1 p
。
我们通常可以忽略 p
,除非涉及定义利用泛型的新类型classes。我们希望 对 D1 ('MetaData ...) V1 p
等类型进行模式匹配,但我们需要某种方式来处理额外参数。
一个技巧是将 D1 ('MetaData ...) V1
视为更高级别的类型(如函子)。这是我们在 GDefault
.
f
class GDefault f where
gdef :: f a
是的,a
将永远是我们永远不会使用的这个愚蠢的参数,但是在 return 中,对于线路噪声,我们能够在我们的实例中对 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 :: f a
,因为类型 class 是在类型 * -> *
上定义的,因此会出现错误消息。
为了利用这个帮手 class,我们做了很多和您一样的事情:
class Default a where
def :: a
default def :: (Generic a, GDefault (Rep a)) => a
def = to gdef
to :: Rep a x -> a
引入了一个虚假的 x
,它与我们的 gdef :: f a
结合生成 f ~ Rep a
,丢弃了 x
并且正是我们想要的.
You can see this approach elaborated in the data-default
package.