Извлечение функции записи в универсальной СОП

В 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 
Другие вопросы по тегам