一个有趣的模式

Mar*_*ark 6 haskell design-patterns

我正在解决99个Haskell Probems.我已成功解决了第21号问题,当我打开解决方案页面时,提出了以下解决方案:

将给定位置的元素插入列表.

insertAt :: a -> [a] -> Int -> [a]
insertAt x xs (n+1) = let (ys,zs) = split xs n in ys++x:zs
Run Code Online (Sandbox Code Playgroud)

我发现模式(n + 1)很有趣,因为它似乎是一种优雅的方式将基于1的参数转换为基于insertAt0的参数split(它的功能来自之前的练习,基本上相同splitAt).问题是GHC没有发现这种优雅的模式,事实上它说:

模式中的解析错误:n + 1

我不认为写这个答案的人是愚蠢的,我想知道这种模式在Haskell中是否合法,如果是,那么如何修复解决方案.

C. *_*ley 12

我相信它已经从语言中删除了,所以很可能在99 Haskell Problems的作者编写该解决方案时,但它已不在Haskell中了.


Jon*_*ast 6

n+k模式的问题可以追溯到Haskell中的设计决策,以通过名称的第一个字符区分构造函数和模式中的变量.如果你回到ML,一个常见的函数定义可能看起来像(使用Haskell语法)

map f nil = nil
map f (x:xn) = f x : map f xn
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,从语法上讲,第一行的LHS 之间f和之间没有区别nil,但它们具有不同的作用; f是一个需要绑定到第一个参数的变量,map而while nil是一个需要与第二个参数匹配的构造函数.现在,ML通过在周围范围内查找每个变量来进行区分,并假设查找失败时名称是变量.因此nil在查找失败时被识别为构造函数.但考虑一下在模式中出现错字时会发生什么:

map f niil = nil
Run Code Online (Sandbox Code Playgroud)

(2个iS IN niil). niil不是作用域中的构造函数名称,因此它被视为变量,并且静默地解释定义.

Haskell解决这个问题的方法是要求构造函数名称以大写字母开头,变量名称以小写字母开头.并且,对于中缀运算符/构造函数,构造函数名称必须以:操作符名称可能不以的开头:.这也有助于区分解构绑定:

x:xn = ...
Run Code Online (Sandbox Code Playgroud)

显然是一个解构绑定,因为你不能定义命名函数:,而

n - m = ...
Run Code Online (Sandbox Code Playgroud)

显然是一个函数定义,因为-它不能是构造函数名.

但是允许n+k模式,比如n+1,意味着+它既是一个有效的函数名,又是一个像模式中的构造函数一样的东西.现在

n + 1 = ...
Run Code Online (Sandbox Code Playgroud)

再次模棱两可; 它可以是命名函数定义的一部分(+),也可以是解构模式匹配定义n.在Haskell 98中,这种模糊性通过声明来解决

n + 1 = ...
Run Code Online (Sandbox Code Playgroud)

一个函数定义,和

(n + 1) = ...
Run Code Online (Sandbox Code Playgroud)

解构约束力.但这显然不是一个令人满意的解决方案.