我正在拉链的顶部实现一些功能,在该拉链中,孔类型已经存在,即,我有这样的东西:
data Zipper (c :: Type -> Constraint) ... =
forall hole. (c hole, ...) =>
Zipper hole ...
其中点表示实施细节,我认为这些细节与我的问题无关。现在考虑一些数据类型:
data Tree = Fork Tree Tree | Leaf Int
我想拥有一种检查我在树上的位置的能力。在简单递归的情况下,实现此目标的标准方法是模式匹配:
case hole of
Fork _ _ -> doSomething
Leaf _ -> doSomethingElse
但是,孔的类型已经存在量化,因此简单的模式匹配就行不通了。我的想法是使用类型类
class WhereAmI p a where
position :: a -> p a
data Position :: Type -> Type where
C_Leaf :: Position Tree
C_Fork :: Position Tree
-- possibly more constructors if we're traversing
-- multiple data structures
然后我可以做类似的事情
f :: Zipper (WhereAmI Position) Tree -> Int
f (Zipper hole _) = case position hole of
C_Leaf -> let (Leaf x) = hole in x
otherwise -> ...
不过,我想用类似这样的魔术将C_Leaf
替换为at @"Leaf"
之类的东西(即使用原始的构造函数名称)
class WhereAmI' p (a :: Symbol) where
position' :: Proxy a -> p
instance WhereAmI' (Position Tree) "Leaf" where
position' _ = C_Leaf
instance WhereAmI' (Position Tree) "Fork" where
position' _ = C_Fork
at :: forall a p. WhereAmI' p a => p
at = position (Proxy :: Proxy a)
除了我不能使用at
作为模式,而且如果我尝试将其设置为模式,GHC抱怨模式中存在解析错误...
是否有一些巧妙的方法来实现我在此要描述的内容?
答案 0 :(得分:1)
据我所知,这实际上是不可能的。没有办法从函数中返回模式以用于匹配,并且由于GADT的优化方式(我认为),似乎除了直接模式匹配之外,不可能做任何更复杂的事情。例如,我失败的尝试之一:
instance Eq (Position a) where
C_Leaf == C_Leaf = True
C_Fork == C_Fork = True
_ == _ = False
pattern At x = ((\y -> at x == y) -> True)
该应该允许您编写case position hole of At @"Leaf" -> ...
,但是它不能进行类型检查,这可能是由于类型细化过程所致。要澄清:
C_Leaf -> ... -- This works
((== C_Leaf) -> True) -> ... -- This doesn't work
y | y == C_Leaf -> ... -- This doesn't work
后两个错误是Couldn't match expected type ‘Tree’ with actual type ‘hole’
。我实际上不确定为什么会发生这种情况,但是我目前的理论是,表达式过于复杂,以至于类型优化无法正确“采用”:就编译器而言,没有理由指望{{1} }总是在==
是错误类型时返回False
(即使我们知道永远不会发生)。所以这是不允许的。
我想知道,为什么您认为使用hole
比使用at @"Leaf"
更好?好像您在一个版本中确实比在另一个版本中真正在“使用原始构造函数名称”那样:它们都使用原始名称,并附加了一些额外的字符。我想在前一种情况下,您可以允许传入一个任意的构造函数符号进行匹配,但是这种情况通常似乎是不允许的,因此无论如何您都不能这样做。据我所知,使用符号方法实际上并不会带来任何好处。
老实说,如果您的C_Leaf
类型将被限制为hole
支持的构造函数,那么我真的看不出要从头开始存在的意义。创建拉链可能包含的所有类型的求和类型并将其作为类型参数传递会更加简单。但是我不太了解您的用例,所以我可能是错的。
如果您真的想要,我想您可能可以使用Template Haskell来做您想要做的事情。那样的话就可以了:
Position
假设您编写了适当的TH函数,则该函数将在编译时转换为$(at "Leaf") -> ...
,这样可以毫无问题地进行编译。
答案 1 :(得分:0)
View Patterns语言扩展允许使用模式中的功能。