我有一个可扩展的黑胶唱片/复合记录(类似于HList,Frames...(,我想生成键/值的元组,例如
tuplify '[String :-> Whatevs, ...] :: [(String, String)]
这出奇地难。原始要点。
解决方案要点,感谢下面的亚历克
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")]
如果您想尝试我到目前为止所做的事情,请查看该要点,它有经过深思熟虑的示例和我看到的错误:
这是用于在复合(reifyDicts
(中重新转换的硬件:
乙烯基(reifyConstraints
(也是如此:
AFAICT,问题是在类似rmap
:
rmap :: (forall x. f x -> g x) -> Rec f rs -> Rec g rs
映射的 fn 被定义为forall x
,但我的tuplify
受到限制,我认为重新化应该将约束移动到类型中(这就是Dict
的用途(,但是,唉,到目前为止没有运气。
我无法在我的全局 Stack 设置上安装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
typeclass 并在一个函数中做所有事情(。
[1] extensions needed for first example
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}