问题描述
这个问题(从 5 年前开始)问 'Why are all recursive pattern synonyms rejected?' 并且它的例子仍然被拒绝。用户指南说 "Pattern synonyms cannot be defined recursively."
我有一个被接受的递归模式同义词(GHC 8.10.2)。我可以调用它,它会循环——这并不奇怪。那么为什么它会编译?/是否有类似的用例?
基于 2016 paper 的第 2.3 节“多态模式同义词”中的示例编写的代码。
data JoinList a = JNil | Unit a | JoinList a `JoinTree` JoinList a deriving (Eq,Read,Show)
我正在尝试定义模式同义词 Nil
。显然 JNil
是匹配项。但也是 JNil ``JoinTree`` JNil
,并且任何 JoinTree
的任意嵌套提供所有叶子都是 JNil
。
pattern Nil :: JoinList a
pattern Nil <- JNil
where Nil = Nil `JoinTree` Nil -- huh?
wot = Nil `JoinTree` Nil -- these all compile
wotwot Nil = ()
wotwotwot = wotwot Nil
wotwotwotwot = wotwot wot
尝试调用 wot
循环,这并不奇怪。尝试致电 wotwotwot
时抱怨 Non-exhaustive patterns in function wotwot
。
完全披露:我在玩什么/我知道这不起作用[另见下文]是:
pattern Nil = JNil -- base case matcher,and builder
where Nil <- Nil `JoinTree` Nil -- recursion on the matcher,not the builder
-- but can't put `<-` equations after where
以下拒绝了Recursive pattern synonym deFinition
——这是我所期望的
pattern NilRec <- NilRec `JoinTree` NilRec
where NilRec = JNil
无论如何这都行不通:它需要首先匹配 NilRec = JNil
作为基本情况。
要回答@Noughtmare 的问题“你会如何建议...”对他的回答发表评论,我想在上面写上“我在玩什么”的声明(是的,我知道这是目前非法的语法);并通过两个案例将其脱糖到匹配器中,依次尝试:
case arg of
{ JNil -> ...
; Nil `JoinTree` Nil -> ...
}
注意这些情况中的每一个都有一个来自 JoinList
类型的数据构造函数。因此,Nil
的递归使用是受保护/中介的,就像通过 ViewPattern 的任何方法一样。 (这是 5 年前的 q 所问的。)换句话说:我认为编译器可以从 pattern ... = ... where ... <- ...
生成一个 ViewPattern。
解决方法
只有匹配器不能递归,因为这可能导致无限模式匹配(无限大的程序)。 case x of Nil -> y
的脱糖将是:
case x of
JNil -> y
JoinTree x2 x3 -> case x2 of
JNil -> case x3 of
JNil -> y
JoinTree x4 x5 -> case x4 of
...
无限前进。
拥有递归构建器很好,因为它们可以包含任意函数。这里也许是一个更简单的例子,可以在不混淆构造函数的情况下展示这一点:
pattern Foo <- ()
where Foo = let loop = loop in loop
您可以使用 ViewPatterns
允许模式中的任意函数:
normJoinList (JoinTree (normJoinList -> JNil) (normJoinList -> JNil)) = JNil
normJoinList x = x
pattern Nil <- (normJoinList -> JNil)
where Nil = JNil
我认为这可以满足您的要求:
> case Nil `JoinTree` Nil of Nil -> True; _ -> False
True