Как я могу ограничить виниловые / композитные записи?
У меня есть расширяемая виниловая / композитная запись (похожая на HList, Frames...), и я хотел бы генерировать кортежи ключей / значений, такие как
tuplify '[String :-> Whatevs, ...] :: [(String, String)]
Это на удивление сложно. оригинальный смысл.
Решение Gist, спасибо Алеку ниже
type FA = "a" :-> String
type FB = "b" :-> Int
type AB = '[FA, FB]
ab :: Rec Identity AB
ab = "A" :*: 1 :*: RNil
tuplify :: (Show a) => Rec Identity '[a] -> [(String, String)]
tuplify = recordToList . rmap undefined -- ??????
-- tuplify ab = [("a", "A"), ("b", "1")]
Если вы хотите попробовать то, что я уже сделал, посмотрите на эту суть, и в ней есть хорошо продуманные примеры и ошибки, которые я вижу:
Вот аппаратное обеспечение для рефайинга в Composite ( reifyDicts
):
И то же самое для винила ( reifyConstraints
):
AFAICT, проблема в том, что в чем-то вроде rmap
:
rmap :: (forall x. f x -> g x) -> Rec f rs -> Rec g rs
Сопоставленный fn определен forall x
, но мой tuplify
ограничен, и я думаю, что reification должен переместить ограничение в тип (вот что Dict
s для), но, увы, пока не повезло.
1 ответ
Я не могу получить composite
связанные вещи для установки в моей глобальной установке стека, но следующее все равно должно работать (я просто скопировал соответствующие определения). Тем не менее, я думаю, что простая диспетчеризация, основанная на типе класса, здесь более проста (поскольку ограничения нетривиальны). Когда все нужные расширения включены [1], вам просто нужно:
class Tuplify a where
tuplify :: a -> [(String, String)]
instance Tuplify (Rec Identity '[]) where
tuplify RNil = []
instance (Show t, KnownSymbol s, Tuplify (Rec Identity rs)) =>
Tuplify (Rec Identity (s :-> t ': rs)) where
tuplify (v :*: rs) = (symbolVal (Proxy :: Proxy s), show v) : tuplify rs
Затем в GHCi:
ghci> tuplify ab
[("a","\"A\""),("b","1")]
Если вы действительно хотите попробовать метод ограничивающего ограничения, начните с объявления класса типа и экземпляра для конкретного требуемого ограничения:
class ShowField a where
showField :: a -> (String, String)
instance (KnownSymbol s, Show a) => ShowField (Identity (s :-> a)) where
showField (Identity (Val v)) = (symbolVal (Proxy :: Proxy s), show v)
Тогда это становится более простым в использовании reifyConstraints
а также rmap
:
tuplify' :: RecAll Identity rs ShowField => Rec Identity rs -> [(String, String)]
tuplify' xs = recordToList
. rmap (\(Vinyl.Compose (Dict x)) -> Vinyl.Const $ showField x)
$ reifyConstraint (Proxy :: Proxy ShowField) xs
Я думаю, что нечто подобное возможно с reifyDicts
хотя я бы хотел, чтобы был определен вариант с использованием ValuesAllHave
вместо просто AllHave
(тогда мы могли бы обойти ShowField
Тип класса и сделать все только функции).
[1] extensions needed for first example
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}