使用 as 语法进行惰性模式匹配的语法

Syntax for lazy pattern matching with `as` syntax

在(原版)GHCi 8.6.5 中,以下函数完全有效:

f xs@ ~(x:xt) = xs

如果我现在在 9.0.1 中做同样的事情,我会得到一个错误

Suffix occurrence of @. For an as-pattern, remove the leading whitespace.

仅仅去掉@~之间的白色space似乎是不够的,因为那样@~会被解释为运算符,所以唯一的我发现的有效变体是

f xs@(~(x:xt)) = xs

我想知道以下内容,但我在更改说明中找不到答案:

  1. 从 8.6.5 到 9.0.1 的具体变化是什么引入了这种不兼容性?
  2. xs@(~(x:xt))真的是编写此模式的最佳方法吗?还是有更好的方法?

描述了 GHC 9.0 中对 ~ 和 @ 处理的更改 here。引用迁移指南:

GHC 9.0 implements Proposal 229, which means that the !, ~, and @ characters are more sensitive to preceding and trailing whitespace than they were before. As a result, some things which used to parse one way will now parse differently (or throw a parse error).

加上括号(variable@(~pattern))是一个很好的解决办法。或者,您可以使用 letwhere 绑定,或单独的惰性 case:

  1. rehead :: a -> [a] -> [a]
    rehead x' xs0 = x' : xs
      where
        _x : xs = xs0
    
  2. rehead :: a -> [a] -> [a]
    rehead x' xs0 = let
     _x : xs = xs0
     in x' : xs
    
  3. {-# Language PatternGuards #-}
    
    rehead :: a -> [a] -> [a]
    rehead x' xs0
      | let _x : xs = xs0
      = x' : xs
    

    如果您想在后续守卫中使用这些绑定,这将非常有用。

  4. rehead :: a -> [a] -> [a]
    rehead x' xs0 = case xs0 of
      ~(_x : xs) -> x' : xs
    

所有这些选项都是最懒惰的:

  • head (rehead 5 [1, 2])
  • = head (rehead 5 [])
  • = head (rehead 5 undefined)
  • = 5

如果您使用 {-# Language Strict #-},则必须将 let/where 绑定写为 ~(_x : xs) = xs0 以允许 [],并且列表参数绑定为 ~xs0 以允许 undefined;要使用 case 获得 irrefutable 模式(不仅仅是懒惰),您必须编写 ~(~(_x : xs)).