haskell - 模式绑定:为什么允许递归模式?
问题描述
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:
[ Exception
GHCi 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 函数而不是实际模式。
解决方案
数据可以像 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 实例可能会在此处终止。
推荐阅读
- asp.net - 在 .net 核心 Restful API 中,如何更改 URL 格式以获取具有单个方法名称的双参数?
- java - spring mongo 中是否有任何内置函数可用于从具有一对一关系的两个不同文档中提取数据?
- jsf - JSoup 和 JSF .... 我正在丢失我当前的对象/生成的新会话
- javascript - React typescript 属性“map”在“用户”类型上不存在
- jmeter - 在 Jmeter 中使用哪个控制器?
- html - 当我输入 align-items:center 我的 svg 图标消失
- c# - 当我按下 Enter 按钮时,如何在数据网格视图单元格之间移动?
- git - 在解决 GIT 中的合并冲突时,有什么方法可以获取我所做的最新更改?
- unit-testing - Golang 中的 Mock Redis 集群服务器
- javascript - 向数据库发送 true 或 false 复选框是否选中