【问题标题】:Why are recursive pattern synonyms accepted?为什么接受递归模式同义词?
【发布时间】:2021-07-24 10:03:35
【问题描述】:

这个问题(从 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 的 q“你会如何建议......”对他的回答的评论,我想写上面的 decl '我在玩什么'(是的,我知道这是目前非法的语法);并将其脱糖到具有 两个 案例的匹配器中,依次尝试:

case arg of
  { JNil -> ...
  ; Nil `JoinTree` Nil -> ...
  }

请注意,每种情况都有一个来自JoinList 类型的数据构造函数。因此,Nil 的递归使用受到保护/中介,就像通过 ViewPattern 的任何方法一样。 (这是 q 5 年前的问题。)换一种说法:我认为编译器可以从 pattern ... = ... where ... &lt;- ... 生成 ViewPattern。

【问题讨论】:

  • 请注意,pattern ... &lt;- ... where ... = ... 表示法分别定义了匹配器(带箭头)和构建器(带等号),它不是您似乎建议的基本案例递归案例拆分。
  • “将其脱糖到具有两个案例的匹配器”——但您建议的脱糖仍然包含糖!
  • @Daniel "你的 [我的] 建议脱糖" 我在这里做了一个尝试gitlab.haskell.org/ghc/ghc/-/issues/12203#note_366851

标签: haskell pattern-synonyms


【解决方案1】:

只有匹配器不能递归,因为这可能导致无限模式匹配(无限大的程序)。 case x of Nil -&gt; 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

【讨论】:

  • 谢谢,论文已经有了使用ViewPatterns 的解决方案。我恨他们。我试图避开它们。
  • @AntC 我添加了一个示例,说明脱糖将如何无限长。您如何建议在不使用递归函数(需要ViewPatterns)的情况下避免这种情况?
  • 我想人们可以想象像let f ts x0 = case x0 of { JNil -&gt; g 0 ts; JoinTree x1 x2 -&gt; f (x2:ts) x1 }; g n [] = z; g n (t:ts) = h n t (g (n+1) ts); h 0 JNil k = k; h n (JoinTree x0 x1) = h (n-1) x0 (h (n-1) x1 k); h _ _ k = z in f [] x 这样的东西(其中z 是在原始情况下进行其余匹配的术语)。直觉:f 检查其参数是否是任意深度的JNils 的完美平衡树; g 检查它的参数是否是一个深度增加的完美平衡树的列表; h 检查它的参数是否是给定深度的完美平衡树。
  • 请注意,模式同义词的实际实现很好地处理递归匹配(大概有点摆弄)。只是没有人费心正确地指定它并依赖于无法完全处理它的脱糖。 pattern Nil &lt;- JNil; pattern Nil &lt;- Nil `JoinTree` Nil,如果有效,应该产生matchNil :: forall rep (r :: TYPE rep) a. JoinList a -&gt; (Void# -&gt; r) -&gt; (Void# -&gt; r) -&gt; r; matchNil JNil y n = y void#; matchNil (l `JoinTree` r) y n = matchNil l (\_ -&gt; matchNil r y n) n; matchNil _ y n = n void#
猜你喜欢
  • 2012-02-16
  • 2011-03-29
  • 2022-01-20
  • 2021-12-20
  • 2014-11-02
  • 1970-01-01
  • 2014-01-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多