根据 Haskell 中的运行时比较创建类型 class 的实例

Creating instances of a type class based on runtime comparisons in Haskell

按照描述的技术 here, 我一直在使用 constraintsreflection 包来 创建类型 class Rel 的本地实例,表示(在类型级别)与类型 t.

的值的关系

现在我要根据运行时值的比较创建实例,允许需要这些比较的代码在运行时将此要求表达为约束,其中值由某种类型级别表示 'variable'.我可以 "sort of" 使用以下代码完成此操作。

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 returns 为真,则会创建类型为 class Rel 的实例 Def Rel ... 来表示这些值在关系中的事实,其中 v1 和 v2在类型级别由两个 id 为 0 和 1 的变量表示。

此代码按我的预期编译和运行(在我的项目中),但问题是如果多个 usingIf 应用程序组合在一起,变量的 ID 将不是唯一的。例如,我真正需要的是一种跟踪有多少实例已添加到 Rel 字典或类似的东西的方法,以便我可以为每个 Var 创建新的 ID。

关于如何解决这个问题有什么建议吗?我不想使用模板 Haskell,但如果这是 only/cleanest 选项,我愿意。

一个可能的技巧是挂钩类型系统中已经存在的生成性。

下面的函数是ill-typed:

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

尽管 b 在某些实例中实际上可能与 a 相同,但由于它也可能不同,因此 ab 被视为不同的类型。特别是对于更高级别的类型,其实现方式是编译器为每个量化类型变量动态构建一个类型,因此 x :: Fresh_ay :: Fresh_b。这些 Fresh_a 是编译器抱怨的 "rigid" 变量。本质上,双重的事情发生在打开存在主义的时候。事实上,我将使用存在主义,因为它看起来更清晰、更明确。

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

我没有你的代码,所以我不知道这是否真的适合你,但这就是我开始攻击它的方式。你可能需要稍微按摩一下。