我开始学习Haskell,主要是为了自己的启发,我喜欢很多支持这种语言的思想和原则。在上一门语言理论课程中,我们玩过Lisp,我对函数式语言产生了浓厚的兴趣。我一直听说Haskell可以提高生产力,所以我决定自己探究一下。到目前为止,我喜欢这种语言,除了一件事情,那就是这些该死的函数签名。
我的专业背景主要是面向对象编程,尤其是Java。我工作的大多数地方都深入推崇现代标准教条,如敏捷开发、代码整洁、测试驱动开发等。在这样的方式下工作了几年后,它已经成为了我的舒适区;特别是"好"的代码应该是自文档化的这个想法。我已经习惯了在集成开发环境中工作,在那里长而冗长的方法名和非常描述性的签名不是问题,因为有智能自动补全和大量分析工具来浏览包和符号;如果我可以在Eclipse中按Ctrl+空格键,然后从方法的名称和与其参数相关联的本地作用域变量推断出它在做什么,而不是查看JavaDocs,那么我就会非常高兴。
这明显不是Haskell社区最佳实践的一部分。我阅读了很多不同意见,我理解Haskell社区认为简洁性是一个优点。我查看了如何阅读Haskell,我理解了很多决策背后的原理,但这并不意味着我喜欢它们;单个字母的变量名等对我来说不是很有趣。我承认如果我想继续使用这种语言进行编程,我必须适应这种情况。
但我无法接受函数签名。例如,从学习Haskell[...]的函数语法部分引用以下示例:
bmiTell :: (RealFloat a) => a -> a -> String
bmiTell weight height
| weight / height ^ 2 <= 18.5 = "You're underweight, you emo, you!"
| weight / height ^ 2 <= 25.0 = "You're supposedly normal. Pffft, I bet you're ugly!"
| weight / height ^ 2 <= 30.0 = "You're fat! Lose some weight, fatty!"
| otherwise = "You're a whale, congratulations!"
我知道这只是一个愚蠢的例子,只是为了解释守卫和类约束。但如果你仅仅看这个函数的签名,你就不知道哪个参数是重量或高度。即使你使用Float或Double代替任何类型,也不会立即可辨。
起初,我认为我会很聪明地使用更长的类型变量名称和多个类约束来欺骗它。
bmiTell :: (RealFloat weight, RealFloat height) => weight -> height -> String
这个命令出现了一个错误(顺便提一下,如果有人能够解释这个错误,我会非常感激):
Could not deduce (height ~ weight)
from the context (RealFloat weight, RealFloat height)
bound by the type signature for
bmiTell :: (RealFloat weight, RealFloat height) =>
weight -> height -> String
at example.hs:(25,1)-(27,27)
`height' is a rigid type variable bound by
the type signature for
bmiTell :: (RealFloat weight, RealFloat height) =>
weight -> height -> String
at example.hs:25:1
`weight' is a rigid type variable bound by
the type signature for
bmiTell :: (RealFloat weight, RealFloat height) =>
weight -> height -> String
at example.hs:25:1
In the first argument of `(^)', namely `height'
In the second argument of `(/)', namely `height ^ 2'
In the first argument of `(<=)', namely `weight / height ^ 2'
由于不完全理解为什么它没有起作用,我开始在谷歌上搜索,甚至找到了这篇小文章,建议使用命名参数,具体来说是通过newtype
欺骗命名参数, 但那似乎有点过头了。
是否没有可接受的方法来构建信息丰富的函数签名?“Haskell Way”难道只是在每个地方都要加上注释吗?
newtype
模拟命名参数”的方法。特别是对于这种宽度/高度
问题。它使代码清晰易懂、更易于维护,并且强烈有助于尺寸分析。如果你做得对,样板代码很少,类型签名更有用,而且错误更少。 - John L(RealFloat weight, weight ~ height) => ...
- Sarah