Можно ли перечислить имена и типы полей в типе данных записи, производном Generic?
Я знаю, что для типов данных, производных данных.Дейта,constrFields
дает список имен полей. Глядя на GHC.Документация Generics, я думаю, что то же самое должно быть возможно для Generic
как хорошо. (но, к сожалению, не смог понять, как это сделать сам).
более конкретно, я ищу две вещи:
Список всех полей записи
... в рамках программы Haskell. Я знал это!--24-->aeson способен автоматически выводить представление JSON любого типа данных записи, производного Generic
, но чтение его исходного кода только подтвердило, что я здесь невежественен. Из того, что я могу предположить, Эсон должен иметь возможность получить все имена полей (как String
или ByteString
s) из типа данных записи, а также их типов (который имеет тип чего-то вроде TypeRep
в данных.Typeable, или экземпляр Eq
: все, что может быть использовано для case
сопоставление шаблонов блоков).
I смутно предположим, что создание класса и экземпляров для M1
, :*:
, etc. это путь, но я не мог добраться до проверки типов.
Проверьте селектор записи
чтобы получить тип данных записи, к которому он принадлежит, имя поля записи (как String
) и т. д.
например,
data Record = Record
{ recordId :: Int32
, recordName :: ByteString
} deriving Generic
функция magic
это как
typeOf (Record {}) == typeOf (magic recordId)
возможны ли они с deriving Generic
, или мне нужно прибегнуть к шаблону Хаскелл?
1 ответов
Список всех полей записи
это очень возможно, и это действительно сделал рекурсией по структуре Rep
, используя класс. Решение ниже работает для типов с одним конструктором и возвращает пустые имена строк для полей без селекторов:
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ScopedTypeVariables #-}
import Data.ByteString (ByteString)
import Data.Data
import Data.Int
import Data.Proxy
import GHC.Generics
import qualified Data.ByteString as B
data Record = Record { recordId :: Int32, recordName :: ByteString }
deriving (Generic)
class Selectors rep where
selectors :: Proxy rep -> [(String, TypeRep)]
instance Selectors f => Selectors (M1 D x f) where
selectors _ = selectors (Proxy :: Proxy f)
instance Selectors f => Selectors (M1 C x f) where
selectors _ = selectors (Proxy :: Proxy f)
instance (Selector s, Typeable t) => Selectors (M1 S s (K1 R t)) where
selectors _ =
[ ( selName (undefined :: M1 S s (K1 R t) ()) , typeOf (undefined :: t) ) ]
instance (Selectors a, Selectors b) => Selectors (a :*: b) where
selectors _ = selectors (Proxy :: Proxy a) ++ selectors (Proxy :: Proxy b)
instance Selectors U1 where
selectors _ = []
теперь мы имеем:
selectors (Proxy :: Proxy (Rep Record))
-- [("recordId",Int32),("recordName",ByteString)]
наименее очевидная часть здесь selName
и Selector
: этот класс можно найти в GHC.Generics
, и это позволяет нам извлекать имена селекторов из сгенерированные типы селекторов. В случае Record
, представление
:kind! Rep Record
Rep Record :: * -> *
= D1
Main.D1Record
(C1
Main.C1_0Record
(S1 Main.S1_0_0Record (Rec0 Int32)
:*: S1 Main.S1_0_1Record (Rec0 ByteString)))
и типы селекторов Main.S1_0_0Record
и Main.S1_0_1Record
. Мы можем получить доступ только к этим типам, извлекая их из Rep
введите с помощью классов или семейств типов, потому что GHC не экспортирует их. Во всяком случае,selName
возвращает нам имя селектора из любого M1
узел с s
тег селектора (он имеет более общего типа t s f a -> String
но это нас здесь не касается).
он также возможно обрабатывать несколько конструкторов и иметь selectors
возвращение [[(String, TypeRep)]]
. В этом случае у нас, вероятно, было бы два класса, один похожий на предыдущий, используемый для извлечения селекторов из данного конструктора, и другой класс для сбора списков для конструкторов.
Проверьте селектор записи
легко получить тип записи из функции:
class Magic f where
magic :: f -> TypeRep
instance Typeable a => Magic (a -> b) where
magic _ = typeOf (undefined :: a)
или статически:
type family Arg f where
Arg (a -> b) = a
однако, без TH мы не можем знать является ли функция законным селектором или просто функцией с правильным типом; они неразличимы в Haskell. Невозможно проверить имя "recordId" в magic recordId
.