我有一个可扩展的乙烯基/复合记录(类似于HList,Frames ...),我想生成键/值的元组,例如
tuplify '[String :-> Whatevs, ...] :: [(String, String)]
这非常困难。 original gist
Solution Gist, thanks to Alec below
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")]
如果你想尝试我到目前为止所做的事情,请查看gist,它有经过深思熟虑的示例和我看到的错误:
以下是复合(reifyDicts
):
对于Vinyl (reifyConstraints
):
AFAICT,问题在于rmap
:
rmap :: (forall x. f x -> g x) -> Rec f rs -> Rec g rs
映射的fn定义为forall x
,但我的tuplify
受到限制,我认为具体化应该将约束移动到类型中(Dict
是什么{{1}}但是,唉,到目前为止没有运气。
答案 0 :(得分:2)
我无法在我的全局堆栈设置中安装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")]
如果你真的想尝试使用reifying约束方法,那么你必须首先为你想要的特定约束声明一个类型类和实例:
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
来定义它的变体(然后我们可以绕过声明{{1} typeclass并只在一个函数中完成所有事情。)
ShowField