了解如何构建 GHC.Generics 代表并转换回值
Understanding how to construct GHC.Generics Rep's and convert back to values
我正在尝试了解如何使用 GHC.Generics
。一个引人入胜但又令人生畏的话题。
在阅读博客条目 24 Days of GHC Extensions: DeriveGeneric 时,我学会了如何取值并导航其 Rep
。好的
然而,阅读博文Building data constructors with GHC Generics which describes the analog of constructing the Rep
and converting it back to a value, I got stumped. I've read through a number of other resources,却没有多大帮助。
博客条目中有以下代码。首先,构造 Rep
:
class Functor f => Mk rep f | rep -> f where
mk :: f (rep a)
instance Mk (K1 i c) ((->) c) where
mk = \x -> K1 x
instance (Mk l fl, Mk r fr) => Mk (l :*: r) (Compose fl fr) where
mk = Compose (fmap (\l -> fmap (\r -> l :*: r) mk) mk)
instance (Mk f f') => Mk (M1 i c f) f' where
mk = M1 <$> mk
然后,处理 Compose
:
class Functor f => Apply f a b | f a -> b where
apply :: f a -> b
instance Apply ((->) a) b (a -> b) where
apply = id
instance (Apply g a b, Apply f b c) => Apply (Compose f g) a c where
apply (Compose x) = apply (fmap apply x)
然后处理类型歧义:
type family Returns (f :: *) :: * where
Returns (a -> b) = Returns b
Returns r = r
make :: forall b f z. (Generic (Returns b), Apply f (Returns b) b, Mk (Rep (Returns b)) f) => b
make = apply (fmap (to :: Rep (Returns b) z -> (Returns b)) (mk :: f (Rep (Returns b) z)))
哇。
真的,我一开始就卡住了,在 class Mk
那里 mk
returns 一个仿函数。我的问题:
mk
返回了什么?为什么它是函子?其结果的解释是什么?我可以看到 Mk
returns 的 K1 i c
实例是一个函数(我知道这是一个仿函数),它接受一个值并将其包装在 K1
中,但是 mk
对于 Mk (l :*: r)
和 Mk (M1 i c f)
我完全迷失了。
我猜 Compose
来自 Data.Functor.Compose
,这意味着当我做 fmap f x
时,它会做 fmap
两层深进入组成的函子。但是我无法理解 Compose
中嵌套的 fmap
。
对于M1 i c f
的实例,我认为它只是将内部值包装在M1
中,所以需要M1 <$> mk
或fmap M1 mk
对我来说毫无意义。
显然,我并不是在摸索这些实例的意图或意义,以及这些实例如何相互作用以创建最终的 Rep
。我希望有人能启发我并提供一个很好的解释如何使用GHC.Generics
。
- What is
mk
returning?
让我们先看一下 GHC.Generics 文档中的一个更简单的示例。为了实现通用函数 encode :: Generic a => a -> [Bool]
位序列化每个具有通用实例的数据类型,他们在下面定义了类型 class :
class Encode' rep where
encode' :: rep p -> [Bool]
通过为每个 Rep 类型(M1、K1 等)定义 Encode'
个实例,他们使该函数可以在所有数据类型上通用。
在Building data constructors with GHC Generics中,作者的最终目标是泛型函数make :: Generic a => TypeOfConstructor a
,所以天真的人可以定义:
class Mk rep where
mk :: (? -> p) -- what should '?' be?
很快意识到由于一些问题这是不可能的:
->
,haskell 中的函数类型一次只接受一个参数,因此 mk
将无法 return 任何有意义的构造函数接受多个参数。
- 参数的数量和类型不清楚:它与所关注的
rep
类型有关。
- 结果类型不能是普通的
p
。如果没有 rep
上下文,就不可能为 :*:
或 :+:
派生实例,并且该函数将不再适用于任何嵌套数据类型。
问题 1 可以用 Data.Functor.Compose 解决。 a -> b -> c
类型的函数可以编码为 Compose ((->) a) ((->) b) c
,它可以进一步组合,同时保留大量关于参数类型的信息。通过将其设为 Mk
的类型参数,问题 2 也得到解决:
class Functor f => Mk rep f | rep -> f where
mk :: f (rep p)
其中 f
是对 Compose f g
和 (->) a
的泛化,它包含构造 rep p
的类型级信息,即最终 [=19= 之前的所有内容] 在 a -> b -> c -> ... -> rep p
。
- I'm guessing
Compose
comes from Data.Functor.Compose
, which means that when I do fmap f x
, it does the fmap
two levels deep into the composed functors. But I can't make sense of the nested fmap
s inside the Compose
.
在 :*:
的 Mk
实例中:
instance (Mk l fl, Mk r fr) => Mk (l :*: r) (Compose fl fr) where
mk = Compose (fmap (\l -> fmap (\r -> l :*: r) mk) mk)
fmap
仅更改嵌套 Compose 的最内层类型,在本例中更改 n 元函数的最终结果。这里的mk
实际上是拼接两个参数列表fl
和fr
,将它们的结果放入一个产品类型中,即
f :: Compose ((->) a) ((->) b) (f r)
g :: Compose ((->) c) ((->) d) (g r)
mk f g :: Compose (Compose ((->) a) ((->) b)) (Compose ((->) c) ((->) d)) ((:*:) f g r)
-- or unwrapped and simplified
(a -> b -> r) -> (c -> d -> r') -> a -> b -> c -> d -> (r, r')
- For the instance of
M1 i c f
, I thought it would just wrap the inner values in M1
, so the need to M1 <$> mk
or fmap M1 mk
makes no sense to me.
它只是将内部值包装在M1
中,但不清楚底层f
的参数列表有多长。如果它接受一个参数,那么 mk
是一个函数,否则它是一个 Compose。 fmap
包裹了它们的最内层值。
我正在尝试了解如何使用 GHC.Generics
。一个引人入胜但又令人生畏的话题。
在阅读博客条目 24 Days of GHC Extensions: DeriveGeneric 时,我学会了如何取值并导航其 Rep
。好的
然而,阅读博文Building data constructors with GHC Generics which describes the analog of constructing the Rep
and converting it back to a value, I got stumped. I've read through a number of other resources,却没有多大帮助。
博客条目中有以下代码。首先,构造 Rep
:
class Functor f => Mk rep f | rep -> f where
mk :: f (rep a)
instance Mk (K1 i c) ((->) c) where
mk = \x -> K1 x
instance (Mk l fl, Mk r fr) => Mk (l :*: r) (Compose fl fr) where
mk = Compose (fmap (\l -> fmap (\r -> l :*: r) mk) mk)
instance (Mk f f') => Mk (M1 i c f) f' where
mk = M1 <$> mk
然后,处理 Compose
:
class Functor f => Apply f a b | f a -> b where
apply :: f a -> b
instance Apply ((->) a) b (a -> b) where
apply = id
instance (Apply g a b, Apply f b c) => Apply (Compose f g) a c where
apply (Compose x) = apply (fmap apply x)
然后处理类型歧义:
type family Returns (f :: *) :: * where
Returns (a -> b) = Returns b
Returns r = r
make :: forall b f z. (Generic (Returns b), Apply f (Returns b) b, Mk (Rep (Returns b)) f) => b
make = apply (fmap (to :: Rep (Returns b) z -> (Returns b)) (mk :: f (Rep (Returns b) z)))
哇。
真的,我一开始就卡住了,在 class Mk
那里 mk
returns 一个仿函数。我的问题:
mk
返回了什么?为什么它是函子?其结果的解释是什么?我可以看到Mk
returns 的K1 i c
实例是一个函数(我知道这是一个仿函数),它接受一个值并将其包装在K1
中,但是mk
对于Mk (l :*: r)
和Mk (M1 i c f)
我完全迷失了。我猜
Compose
来自Data.Functor.Compose
,这意味着当我做fmap f x
时,它会做fmap
两层深进入组成的函子。但是我无法理解Compose
中嵌套的fmap
。对于
M1 i c f
的实例,我认为它只是将内部值包装在M1
中,所以需要M1 <$> mk
或fmap M1 mk
对我来说毫无意义。
显然,我并不是在摸索这些实例的意图或意义,以及这些实例如何相互作用以创建最终的 Rep
。我希望有人能启发我并提供一个很好的解释如何使用GHC.Generics
。
- What is
mk
returning?
让我们先看一下 GHC.Generics 文档中的一个更简单的示例。为了实现通用函数 encode :: Generic a => a -> [Bool]
位序列化每个具有通用实例的数据类型,他们在下面定义了类型 class :
class Encode' rep where
encode' :: rep p -> [Bool]
通过为每个 Rep 类型(M1、K1 等)定义 Encode'
个实例,他们使该函数可以在所有数据类型上通用。
在Building data constructors with GHC Generics中,作者的最终目标是泛型函数make :: Generic a => TypeOfConstructor a
,所以天真的人可以定义:
class Mk rep where
mk :: (? -> p) -- what should '?' be?
很快意识到由于一些问题这是不可能的:
->
,haskell 中的函数类型一次只接受一个参数,因此mk
将无法 return 任何有意义的构造函数接受多个参数。- 参数的数量和类型不清楚:它与所关注的
rep
类型有关。 - 结果类型不能是普通的
p
。如果没有rep
上下文,就不可能为:*:
或:+:
派生实例,并且该函数将不再适用于任何嵌套数据类型。
问题 1 可以用 Data.Functor.Compose 解决。 a -> b -> c
类型的函数可以编码为 Compose ((->) a) ((->) b) c
,它可以进一步组合,同时保留大量关于参数类型的信息。通过将其设为 Mk
的类型参数,问题 2 也得到解决:
class Functor f => Mk rep f | rep -> f where
mk :: f (rep p)
其中 f
是对 Compose f g
和 (->) a
的泛化,它包含构造 rep p
的类型级信息,即最终 [=19= 之前的所有内容] 在 a -> b -> c -> ... -> rep p
。
- I'm guessing
Compose
comes fromData.Functor.Compose
, which means that when I dofmap f x
, it does thefmap
two levels deep into the composed functors. But I can't make sense of the nestedfmap
s inside theCompose
.
在 :*:
的 Mk
实例中:
instance (Mk l fl, Mk r fr) => Mk (l :*: r) (Compose fl fr) where
mk = Compose (fmap (\l -> fmap (\r -> l :*: r) mk) mk)
fmap
仅更改嵌套 Compose 的最内层类型,在本例中更改 n 元函数的最终结果。这里的mk
实际上是拼接两个参数列表fl
和fr
,将它们的结果放入一个产品类型中,即
f :: Compose ((->) a) ((->) b) (f r)
g :: Compose ((->) c) ((->) d) (g r)
mk f g :: Compose (Compose ((->) a) ((->) b)) (Compose ((->) c) ((->) d)) ((:*:) f g r)
-- or unwrapped and simplified
(a -> b -> r) -> (c -> d -> r') -> a -> b -> c -> d -> (r, r')
- For the instance of
M1 i c f
, I thought it would just wrap the inner values inM1
, so the need toM1 <$> mk
orfmap M1 mk
makes no sense to me.
它只是将内部值包装在M1
中,但不清楚底层f
的参数列表有多长。如果它接受一个参数,那么 mk
是一个函数,否则它是一个 Compose。 fmap
包裹了它们的最内层值。