如何限制乙烯基/复合记录?

时间:2017-08-03 02:07:08

标签: haskell types hlist extensible vinyl

我有一个可扩展的乙烯基/复合记录(类似于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}}但是,唉,到目前为止没有运气。

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)

然后使用reifyConstraintsrmap

变得更加直接
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