在Haskell

时间:2016-01-15 18:03:21

标签: haskell reflection types metaprogramming type-constraints

按照here描述的技术, 我一直在使用constraintsreflection个包 创建类型类Rel的本地实例,它表示(在类型级别)类型t的值的关系。

现在我想基于运行时值的比较创建实例,允许在运行时保存需要这些比较的代码将该需求表达为约束,其中值由某种类型级别的变量&表示。 #39 ;.我可以"有点"使用以下代码完成此操作。

usingIf :: t -> t -- Two values of type t to compare 
           -> (forall s1 s2 . Proxy s1 -> Proxy s2 -> Rel (Var s1) (Var s2) => b) 
              -- ^ Then branch: a term with a constraint that requires 
              --   two type-level "variables" to be in a relation 'Rel'
           -> b -- Else branch: the term to evaluate if the values (of type t) are 
                --   not in the relation 
           -> b
usingIf v1 v2 ifTrue ifFalse = 
    if cmp v1 v2 then
      using (Def Rel (DVar (Proxy :: Proxy 0)) (DVar (Proxy :: Proxy 1)))
                 (ifTrue (Proxy :: Proxy 0) (Proxy :: Proxy 1))
    else 
      ifFalse

此代码采用两个t值v1和v2,并对它们进行比较。如果cmp返回true,则创建类型为Rel的实例Def Rel ...以表示这些值在关系中的事实,其中v1和v2在类型级别由两个表示id为0和1的变量。

这段代码按照我的预期编译和运行(在我的项目中),但问题是如果组成多个usingIf应用程序,变量的id将不是唯一的。我真正需要的是,例如,一种跟踪已经添加到Rel字典中的实例数量的方法或者等价物的方法,以便我可以为每个Var创建新的ID。

有关如何解决此问题的任何建议?我不想使用模板Haskell,但我愿意,如果这是唯一/最干净的选择。

1 个答案:

答案 0 :(得分:1)

一个可能的技巧是引入类型系统中已存在的生成。

以下功能是错误输入的:

f :: a -> b -> a
f x y = x `asTypeOf` y

即使b在某些实例化中可能与a实际上相同,但由于它也可能不同,ab被视为不同的类型。特别是对于更高级别的类型,实现它的方式是编译器为每个量化类型变量动态组成一个类型,因此x :: Fresh_ay :: Fresh_b。这些Fresh_a是编译器抱怨的“刚性”变量。从本质上讲,在打开存在主义时会发生双重事情。事实上,我会使用一个存在主义,因为它似乎更清晰,更明确。

-- Needs ExistentialQuantification and PolyKinds, but the same effect
-- could be accomplished without either.
data Gen = forall (n :: k). Gen (Proxy n)
gen = Gen Proxy -- Probably need to turn off the monomorphism restriction

usingIf :: t -> t
        -> (forall s1 s2 . Proxy s1 -> Proxy s2 -> Rel (Var s1) (Var s2) => b)
        -> b 
        -> b
usingIf v1 v2 ifTrue ifFalse = 
    if cmp v1 v2 then
      case gen of Gen p -> -- the compiler has forgotten what went in to gen so it must
          case gen of Gen q -> -- assume p and q have types distinct from everything
              using (Def Rel (DVar p) (DVar q)) (ifTrue p q)
    else 
      ifFalse

我没有你的代码,所以我不知道这对你是否真的有用,但是我会开始攻击它。你可能需要按摩一下。