Как я могу ограничить виниловые / композитные записи?

У меня есть расширяемая виниловая / композитная запись (похожая на 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       #-}
Другие вопросы по тегам