首页 > 解决方案 > 模式绑定:为什么允许递归模式?

问题描述

Prelude> let (Just x) = Just (x - 1) in x
*** Exception: <<loop>>

为什么编译器接受该声明?它可以从语法上看出它绑定到循环(?)

GHC 知道:

Prelude> :set -XBangPatterns
Prelude> let !(Just x) = Just (x - 1) in x
    Recursive bang-pattern or unboxed-tuple bindings aren't allowed:

[ ExceptionGHCi 7.10.2 中的编译器失败消息。在 GHC 8.10 中没有有用的消息,只是循环。]

递归模式绑定有一些合理的用途吗?我认为对于模式绑定 decl,lhs 的自由变量应该与 rhs 的自由变量不相交(?)

通过“模式绑定”,我的意思是,根据 Haskell Language Report 2010,第 4.4.3.2 节,特别是从数据构造函数/在最外层范围开始的 lhs。

(我希望比 lhs 上的单个 var 更令人兴奋。这仍然算作“模式绑定”,请参阅评论。)

当然对于函数 decls,编译器必须允许递归,因为它无法从语法上判断函数是否会循环:

Prelude> let { f 0 = 0;
               f x = f (x - 1) }           -- will terminate (for positive inputs)
             in f 5

在我看来,lhs 上的裸变量更像是 niladic 函数而不是实际模式。

标签: haskellpattern-matching

解决方案


数据可以像 Haskell 中的函数一样懒惰。因此,肯定有递归绑定的用例。例如,考虑以下定义fix

fix :: (a -> a) -> a
fix f = let x = f x in x

twoOnes :: [Int]
twoOnes = take 2 $ fix (1 :)

请注意,由于 let-binding 的右侧在左侧是非严格的,因此绑定可以产生结果。

考虑到一些同样愚蠢的情况,即使是你原来的“明显愚蠢”的表达也可能会终止Num

instance Num () where
  fromInteger x = ()
  x - y = ()

loop :: ()
loop = let (Just x) = Just (x - 1) in x

显然对于这种特定类型不是超级有用,但 GHC 并不负责决定哪些合法表达式是有用的,并且通过更多的工作,可以想象更多有用的 Num 实例可能会在此处终止。


推荐阅读