在Haskell中基于运行时比较创建类型类的实例

在Haskell中基于运行时比较创建类型类的实例,haskell,reflection,types,metaprogramming,type-constraints,Haskell,Reflection,Types,Metaprogramming,Type Constraints,按照所描述的技术, 我一直在使用约束和反射包 创建类型类Rel的本地实例,该类表示(在类型级别)类型t的值上的关系 现在,我想基于运行时值的比较创建实例,允许需要在运行时保持这些比较的代码将该要求表示为约束,其中值由某种类型的类型级别“变量”表示。我可以用下面的代码“某种程度上”实现这一点 usingIf :: t -> t -- Two values of type t to compare -> (forall s1 s2 . Proxy s1 ->

按照所描述的技术, 我一直在使用
约束
反射
包 创建类型类
Rel
的本地实例,该类表示(在类型级别)类型
t
的值上的关系

现在,我想基于运行时值的比较创建实例,允许需要在运行时保持这些比较的代码将该要求表示为约束,其中值由某种类型的类型级别“变量”表示。我可以用下面的代码“某种程度上”实现这一点

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,但如果这是唯一/最干净的选择,我愿意使用。

一个可能的技巧是连接到类型系统中已经存在的生成性

以下函数类型不正确:

f :: a -> b -> a
f x y = x `asTypeOf` y
尽管在某些实例化中,
b
实际上可能与
a
相同,但也可能不同,
a
b
被视为不同的类型。特别是对于更高级别的类型,实现方法是编译器为每个量化的类型变量动态生成一个类型,因此
x::Fresh_a
y::Fresh_b
。这些
Fresh_a
s是编译器抱怨的“刚性”变量。本质上,当打开一个存在主义时,双重事物发生了。事实上,我将使用存在主义,因为它看起来更清晰、更明确

-- 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

我没有你的代码,所以我不知道这是否真的适用于你,但这是我将如何开始攻击它。您可能需要稍微按摩一下。

如果返回其他类型,您是否愿意使用
呢?也许它不能处理任何
b
而只能处理表达式类型,然后添加一个类约束,如
。。(对于所有s1 s2.NotOccurs'[s1,s2]r=>Proxy s1->Proxy s2->Rel(Var s1)(Var s2)=>r)
-然后
NotOccurs
类通过获取现有表达式并计算该表达式的免费名称来提供名称。这很有帮助,谢谢!事实上,我最终尝试了类似的方法。打开一个存在主义的例子似乎确实奏效了。为了保留v1和p等之间的关联,我最终进行了重构,以便传递一个带有值和单例类型(而不是代理)的对。这使我能够轻松地在静态和动态世界之间移动,并在类型变量之间建立更复杂的关系。