【问题标题】:How can I constrain Vinyl / Composite Records?如何限制乙烯基/复合记录?
【发布时间】:2017-08-03 02:07:07
【问题描述】:

我有一个可扩展的 Vinyl / Composite 记录(类似于 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,它有经过深思熟虑的示例和我看到的错误:

这是用于在 Composite (reifyDicts): 中重新生成的硬件

乙烯基(reifyConstraints):也是如此

AFAICT,问题在于rmap

rmap :: (forall x. f x -> g x) -> Rec f rs -> Rec g rs

映射的 fn 定义为 forall x,但我的 tuplify 受到约束,我认为具体化应该将约束移动到类型中(这就是 Dicts 的用途),但是,唉,没有运气所以远。

【问题讨论】:

  • 您是否避免使用reifyNames? (这似乎已经为这种事情做好了准备......)

标签: haskell types hlist extensible vinyl


【解决方案1】:

我无法在我的全局 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)

那么使用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(然后我们可以绕过声明ShowField 类型类并只做所有事情一个函数)。


[1] extensions needed for first example

{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE FlexibleInstances   #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies        #-}
{-# LANGUAGE TypeOperators       #-}

【讨论】:

  • composite 库似乎被过度设计,几乎没有任何好处。例如,您声明ValuesAllHave 应该有reifyDicts 的变体;但是如果不声明一个额外的类来证明每个rs 确实是:->(例如class IsAssoc a where isAssoc :: forall r . a -> (forall s v . ((s :-> v) ~ a) => r) -> r),就不可能 - 你的ShowField 提供了这个证明。此外,reifyDicts 使用RecApplicative 重建列表的主干,而不是仅仅对输入进行归纳。 composite 根本没有提供简单的方法来执行这个(简单的)操作!
  • @user2407038 我完全同意。我还尝试积极使用他们提供的机器,我最终得到的解决方案比我发布的解决方案长两倍,而且不太清晰。 ://
  • 谢谢!我是否正确地假设依赖类型,或者甚至只是 TypeInType,可以使所有这一切变得更容易,因为类型、种类和约束 (?) 更多地处于同一级别?
  • 我建议你考虑class Tuplify f ts where tuplify :: Rec f ts -> [(String, String)]。这稍微概括了它,IMO 对其进行了清理。
猜你喜欢
  • 1970-01-01
  • 2015-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-26
相关资源
最近更新 更多