按照here描述的技术,
我一直在使用constraints
和reflection
个包
创建类型类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,但我愿意,如果这是唯一/最干净的选择。
答案 0 :(得分:1)
一个可能的技巧是引入类型系统中已存在的生成。
以下功能是错误输入的:
f :: a -> b -> a
f x y = x `asTypeOf` y
即使b
在某些实例化中可能与a
实际上相同,但由于它也可能不同,a
和b
被视为不同的类型。特别是对于更高级别的类型,实现它的方式是编译器为每个量化类型变量动态组成一个类型,因此x :: Fresh_a
和y :: 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
我没有你的代码,所以我不知道这对你是否真的有用,但是我会开始攻击它。你可能需要按摩一下。