根据 Haskell 中的运行时比较创建类型 class 的实例
Creating instances of a type class based on runtime comparisons in Haskell
按照描述的技术 here,
我一直在使用 constraints
和 reflection
包来
创建类型 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
相同,但由于它也可能不同,因此 a
和 b
被视为不同的类型。特别是对于更高级别的类型,其实现方式是编译器为每个量化类型变量动态构建一个类型,因此 x :: Fresh_a
和 y :: 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
我没有你的代码,所以我不知道这是否真的适合你,但这就是我开始攻击它的方式。你可能需要稍微按摩一下。
按照描述的技术 here,
我一直在使用 constraints
和 reflection
包来
创建类型 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
相同,但由于它也可能不同,因此 a
和 b
被视为不同的类型。特别是对于更高级别的类型,其实现方式是编译器为每个量化类型变量动态构建一个类型,因此 x :: Fresh_a
和 y :: 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
我没有你的代码,所以我不知道这是否真的适合你,但这就是我开始攻击它的方式。你可能需要稍微按摩一下。