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

问题描述

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

f xs@ ~(x:xt) = xs

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

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

仅仅删除 @~间的空白似乎还不够,因为 @~ 会被解释为运算符,所以我发现的唯一有效变体是

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

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

  1. 从 8.6.5 到 9.0.1 究竟发生了什么变化,导致了这种不兼容?
  2. xs@(~(x:xt)) 真的是编写此模式的最佳方式,还是有比此更好的方式?

解决方法

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

GHC 9.0 实现了 Proposal 229,这意味着 !、~ 和 @ 字符对前后空格比以前更敏感。结果,一些过去以一种方式解析的东西现在会以不同的方式解析(或抛出解析错误)。

,

添加括号 (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 获得 无可辩驳的 模式(不仅仅是懒惰),您必须编写 ~(~(_x : xs))