Извлечение функции записи в универсальной СОП
В Sum of Products
подход, как можно получить функцию записи? Пример кода ниже с типом записи (ghc 7.10.3
):
{-# LANGUAGE DeriveGeneric #-}
import qualified GHC.Generics as GHC
import Generics.SOP
data Rec = Rec { frec :: Int, srec :: Maybe String}
deriving (Show, GHC.Generic)
instance Generic Rec -- empty
instance HasDatatypeInfo Rec
Покажи нам DataTypeInfo
по подсказке ghci:
*Main> datatypeInfo (Proxy :: Proxy Rec)
ADT "Main" "Rec" (Record "Rec" (FieldInfo "frec" :* (FieldInfo "srec" :* Nil)) :* Nil)
Мы видим, что frec
а также srec
оба типа FieldInfo
который имеет конструктор FieldInfo
который принимает fieldName
как строка Итак, я не вижу способа получить реальные функции frec :: Rec -> Int
а также srec :: Rec -> Maybe String
, Я также посмотрел на пример шоу, но он не использует функции записи.
Будут благодарны указатели о том, как получить функции записи (может быть HList типа HList '[(Rec -> Int), (Rec -> Maybe String)]
)).
Приложение к вопросу
Я связан узлами типа о том, как получить функции из проекций, используя подход, который выложил user2407038. Итак, я хотел бы добавить к вопросу далее: как мы можем построить функцию, как показано ниже, используя SOP
подход на Rec
конструктор - мы используем как имя поля записи, так и функцию здесь:
[ ("frec" ++) . show . frec, ("srec" ++) . show . srec]
1 ответ
generics-sop
Библиотека реализует общие комбинаторы для работы с суммами продуктов, поэтому вы должны написать такую функцию, используя эти комбинаторы.
Есть одна проблема - generics-sop
не имеет никакой информации о записях и конструкторах на уровне типов, поэтому ваша функция все еще будет частичной (если вы не копаетесь в обобщениях GHC Rep
).
В этом примере я просто выберу частичную функцию route.
Во-первых, вам нужен этот тип данных:
data (:*:) f g x = f x :*: g x deriving (Show, Eq, Ord, Functor)
Кажется, что он должен быть включен в библиотеку, но это не так (или я не могу его найти).
Тип функции будет
recordSelectors :: forall t r . (Code t ~ '[ r ], Generic t, HasDatatypeInfo t)
=> Proxy t -> Maybe (NP (FieldInfo :*: (->) t) r)
Ограничение Code t ~ '[ r ]
просто говорит, что сумма представлений производств t
является одноэлементным списком (один конструктор). Тип возвращаемого значения (может быть) продукт над списком r
(список типов полей записи), где есть FieldInfo x
и t -> x
для каждого типа x
в r
,
Одна реализация
case datatypeInfo (Proxy :: Proxy t) of
ADT _ _ (Record _ fields :* Nil) -> Just $
hzipWith (\nm (Fn prj) -> nm :*: (unI . prj . K . (\(Z x) -> x) . unSOP . from))
fields
projections
_ -> Nothing
Здесь функция определяет в заданном типе данных действительно запись, а в противном случае возвращает Nothing
, Если это запись, нажмите на поле записи и projections
(определяется библиотекой), который определяет проекции для произвольного родового продукта, который по существу просто NP '[ Code Rec -> Int, Code Rec -> Maybe String ]
для вашего типа. Осталось только составить from
работать с каждой проекцией, чтобы получить "реальные" проекции. Остальные (Fn
, unSOP
и т. д.) просто тож.
Поскольку оказывается, что вам просто нужны функции проецирования записи без имен функций, это еще проще. И теперь функция не является частичной - любой тип конструктора имеет "проекции записи".
recordSelectors' :: forall t r . (Code t ~ '[ r ], Generic t)
=> Proxy t -> NP ((->) t) r
recordSelectors' _ = hmap (\(Fn prj) -> unI . prj . K . (\(Z x) -> x) . unSOP . from)
projections