Haskell 在模式同义词中调用函数

Haskell 在模式同义词中调用函数,haskell,Haskell,我在拉链上实现了一些功能,其中孔类型是存在量化的,也就是说,我有这样的东西: data Zipper (c :: Type -> Constraint) ... = forall hole. (c hole, ...) => Zipper hole ... 其中点表示我认为与我的问题无关的实现细节。现在考虑一些数据类型: data Tree = Fork Tree Tree | Leaf Int 我想要的是能够检查我在树上的位置。对于简单递归,实现这一点的标准方法是模

我在拉链上实现了一些功能,其中孔类型是存在量化的,也就是说,我有这样的东西:

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 -> ...
然而,我想要的是用类似于
at@“Leaf”
(即使用原始构造函数名称)的东西替换
C_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会抱怨模式中存在解析错误


有什么聪明的方法可以实现我在这里要描述的吗?

语言扩展允许在模式中使用函数。

据我所知,这是不可能的。没有办法从函数返回模式用于匹配,而且由于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)
这应该允许您编写At@“Leaf”->…的
大小写位置孔,但它不会进行类型检查,可能是因为类型细化过程。澄清:

C_Leaf -> ...                -- This works
((== C_Leaf) -> True) -> ... -- This doesn't work
y | y == C_Leaf -> ...       -- This doesn't work
后两者的错误是
无法将预期类型“Tree”与实际类型“hole”匹配
。我实际上不知道为什么会发生这种情况,但我目前的理论是,表达式太复杂,类型细化无法正确地“采用”:就编译器而言,没有理由期望
==
是错误类型时总是返回
(尽管我们知道这永远不会发生)所以这是不允许的


我想知道,为什么你认为在“Leaf”
上使用
比使用
C\u Leaf
更好?这并不是说你真的在“使用原始的构造函数名”在一个版本中,与在另一个版本中一样:它们都使用原始名称,并附加了一些额外的字符。我想在前一种情况下,您可以允许传入任意构造函数符号来匹配,但这种事情通常是不允许的,所以您无论如何也不能这样做。从我所知,您不会这样做使用符号方法可以获得任何东西

老实说,如果你的
hole
类型将被限制为
Position
支持的构造函数,那么我真的不认为从一开始就将其存在有什么意义。创建拉链可能包含的所有类型的sum类型并将其作为类型参数传递会简单得多。但我不知道你的用例很好,所以我可能错了


如果您真的愿意,我想您可能可以使用Template Haskell来完成您要做的事情。这样可以实现以下功能:

$(at "Leaf") -> ...

假设您编写了适当的TH函数,在编译时,它将被转换成<代码> CyLeave>…,它将编译而不发布。

您已经留下了大量的东西。我知道您认为有些不相关,但它意味着(例如)我实际上无法尝试编译或添加您的代码,因为有一大堆代码丢失了。我甚至不能很好地理解您的
Zipper
的真正含义。@dfeuer很好。这会有帮助吗?我说的Zipper是指Huet最初描述的。链接会有帮助;一段自包含的代码会更好。我是familiar使用Huet的拉链,但它们与您正在构建的拉链结构之间的关系并不明显。”……除了我不能将
at
用作模式,如果我试图将其作为模式,GHC会抱怨模式中存在解析错误……”您知道模式同义词应该大写吗?有一个名为
一流模式的库,它允许将模式视为值:您可以尝试探索使用该库是否可以实现您的目标。@bergey,它允许在“错误的一边”使用函数关于模式,即如果你有
x,我想这就是我想要的答案,谢谢。为了回答你的问题,这应该是EDSL的一部分,我确实觉得
at@“Leaf”
更接近于原始的用户定义构造函数名称。不过你可能是对的,我还是坚持使用
C
版本吧。