Я пытаюсь узнать, как использовать GHC.Generics
< /а>. Интересная тема, но пугающая.
Читая запись в блоге 24 Days of GHC Extensions: DeriveGeneric, я научился принимать значение и перемещаться по его Rep
. Хорошо.
Однако, читая запись в блоге Создание конструкторов данных с помощью GHC Generics, который описывает аналог создания Rep
и преобразования его обратно в значение, я зашел в тупик. Я прочитал a номер из 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)))
Вот это да.
Действительно, я застрял в самом начале, на классе Mk
, где mk
возвращает функтор. Мои вопросы:
Что возвращает
mk
? Почему это функтор? Как интерпретировать его результат? Я вижу, чтоK1 i c
экземплярMk
возвращает функцию (я понимаю, что это функтор), которая принимает значение и оборачивает его вK1
, ноmk
дляMk (l :*: r)
иMk (M1 i c f)
совершенно для меня непонятна.Я предполагаю, что
Compose
происходит отData.Functor.Compose
, что означает, что когда я делаюfmap f x
, он выполняетfmap
два уровня вглубь составных функторов. Но я не могу понять вложенныеfmap
внутриCompose
.Что касается экземпляра
M1 i c f
, я думал, что он просто перенесет внутренние значения вM1
, поэтому необходимостьM1 <$> mk
илиfmap M1 mk
не имеет для меня смысла.
Очевидно, я не вникаю в намерения или значение этих экземпляров и в то, как эти экземпляры взаимодействуют для создания окончательного Rep
. Я надеюсь, что кто-то может просветить меня и дать хорошее объяснение того, как использовать GHC.Generics
по пути.
a -> b -> ... -> R
, вместо цепочкиCompose
, которую генерирует классMk
:Compose ((->) a) (Compose ((->) b) ... R
. Если вы пытаетесь понять дженерики, я думаю, вы можете игнорировать все, кроме классаMk
, который здесь является рабочей лошадкой для дженериков. Я не знаю, почему автор решил написать все с помощью Compose, а затем преобразовать его в поливариативную функцию. - person user2407038   schedule 16.02.2016