我正在尝试了解如何使用GHC.Generics
。一个迷人的话题,但令人生畏。了解如何构建GHC.Generics Rep并将其转换回值
在阅读博客条目24 Days of GHC Extensions: DeriveGeneric时,我学会了如何获取价值并导航其Rep
。好的。
但是,读博客条目Building data constructors with GHC Generics描述了构建Rep
的模拟并将其转换回一个值,我很难过。我已阅读anumberofotherresources,但没有太大的帮助。
在博客条目中是以下代码。首先,构建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)))
哇。
真的,我被卡在最开始,在类Mk
其中mk
返回一个仿函数。我的问题:
什么是
mk
返回?为什么它是仿函数?其结果的解释是什么?我可以看到Mk
的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
是没有意义的我。
很显然,我不是所著的Grokking这些实例的目的或意义以及如何将这些实例进行交互以创建最终Rep
。我希望有人能够启发我,并提供一个很好的解释如何使用GHC.Generics
一路上。
请注意,这里有两个独立的设计“目标”(可以这么说) - 一般地推导一个函数,并使该函数“很好”。作者想编写一个多变量函数,比如'a - > b - > ... - > R',而不是有一个'Compose'链,这就是'Mk'类生成的:'Compose(( - >) a)(撰写(( - >)b)... R'。如果你想了解泛型,我认为你可以忽略除Mk类之外的所有其他类,这是泛型的主力,不知道为什么作者选择使用Compose编写所有内容,然后将其“转换”为多变量函数。 – user2407038