有人可以提供一个链接到一个好的 Haskell 编码标准吗?我找到了 this和this,但它们远非全面。更不用说 HaskellWiki 包含诸如“谨慎使用类”和“定义符号中缀标识符应该只留给图书馆作者”之类的“珍宝”。
5 回答
真的很难的问题。我希望你的回答能带来好的结果。同时,这里是我在初学者代码中发现的错误或其他烦人的目录。Kornel Kisielewicz 指出的加州理工学院风格页面有一些重叠。我的一些建议就像 HaskellWiki “宝石”一样模糊和无用,但我希望至少这是更好的建议 :-)
格式化您的代码,使其适合 80 列。(高级用户可能更喜欢 87 或 88;除此之外正在推动它。)
不要忘记
let
绑定和where
子句会创建相互递归的定义嵌套,而不是定义序列。利用
where
子句,尤其是它们查看已经在范围内的函数参数的能力(很好的模糊建议)。如果你真的很喜欢 Haskell,你的代码应该where
比-bindings 有更多的 -bindingslet
。太多let
的 -bindings 是未重构的 ML 程序员或 Lisp 程序员的标志。避免多余的括号。一些多余的括号特别令人反感的地方是
围绕
if
表达式中的条件(将您标记为未重构的 C 程序员)围绕一个本身就是中缀运算符的参数的函数应用程序(函数应用程序比任何中缀运算符绑定得更紧密。这个事实应该刻在每个 Haskeller 的大脑中,就像我们恐龙拥有 APL 从右到左的扫描规则一样烧了。)
在中缀运算符周围放置空格。在元组文字中的每个逗号后面放置一个空格。
最好在函数和它的参数之间留一个空格,即使参数是带括号的。
明智地使用
$
运算符来减少括号。注意$
和 infix之间的密切关系.
:f $ g $ h x == (f . g . h) x == f . g . h $ x
不要忽视内置
Maybe
和Either
类型。从不写
if <expression> then True else False
;正确的短语是简单的<expression>
。不要使用
head
或tail
何时可以使用模式匹配。不要忽视使用中缀点运算符的函数组合。
小心使用换行符。换行可以提高可读性,但需要权衡:您的编辑器一次只能显示 40-50 行。如果您需要一次阅读和理解一个大型函数,则不能过度使用换行符。
几乎总是更喜欢
--
运行到行尾的评论而不是{- ... -}
评论。大括号注释可能适用于大标题——就是这样。给每个顶级函数一个明确的类型签名。
如果可能,对齐出现在相邻行中的
--
行、=
符号,甚至括号和逗号。受 GHC 中心的影响,我非常倾向于使用
camelCase
导出的标识符并使short_name
用下划线表示本地where
绑定或let
绑定变量。
一些好的经验法则恕我直言:
- 请咨询HLint以确保您没有多余的大括号,并且您的代码不是毫无意义的点满。
- 避免重新创建现有的库函数。Hoogle可以帮助您找到它们。
- 很多时候,现有的库函数比要创建的函数更通用。例如,如果您想要
Maybe (Maybe a) -> Maybe a
,那么join
除其他外就这样做。
- 很多时候,现有的库函数比要创建的函数更通用。例如,如果您想要
- 参数命名和文档有时很重要。
- 对于像
replicate :: Int -> a -> [a]
这样的函数,每个参数的作用非常明显,仅从它们的类型来看。 - 对于接受多个相同类型参数的函数,例如
isPrefixOf :: (Eq a) => [a] -> [a] -> Bool
,参数的命名/文档更重要。
- 对于像
- 如果一个函数的存在只是为了服务另一个函数,并且在其他方面没有用,和/或很难为它想一个好名字,那么它可能应该存在于它的调用者
where
子句中,而不是模块的范围内。 - 干燥
- 适当时使用 Template-Haskell。
- 像
zip3
,zipWith3
,zip4
,zipWith4
, 等等这样的功能包非常棒。请改用带s 的Applicative
样式。ZipList
你可能永远不需要这样的功能。 - 自动派生实例。派生包可以帮助您派生类型类的实例,例如
Functor
(只有一种正确的方法可以使类型成为 的实例Functor
)。
- 更通用的代码有几个好处:
- 它更有用且可重复使用。
- 它不太容易出现错误,因为有更多的限制。
- 例如,如果您想编程
concat :: [[a]] -> [a]
,请注意它如何更通用join :: Monad m => m (m a) -> m a
。编程时出错的空间较小,join
因为在编程时concat
您可能会错误地反转列表,并且join
您可以做的事情很少。
- 例如,如果您想编程
- 在代码中的许多地方使用相同的 monad 转换器堆栈时,请为其创建一个类型同义词。这将使类型更短,更简洁,更容易批量修改。
- 谨防“懒惰的 IO”。例如
readFile
,在读取文件时并没有真正读取文件的内容。 - 避免缩进太多以至于我找不到代码。
- 如果您的类型在逻辑上是类型类的实例,请将其设为实例。
- 该实例可以用熟悉的功能替换您可能考虑过的其他接口功能。
- 注意:如果有多个逻辑实例,请为这些实例创建 newtype-wrappers。
- 使不同的实例保持一致。如果列表
Applicative
表现得像ZipList
.
我喜欢尝试通过执行以下操作来尽可能地将函数组织为无点样式组合:
func = boo . boppity . bippity . snd where boo = ... boppity = ... bippity = ...
我喜欢使用 ($) 来避免嵌套括号或长括号表达式
...我以为我还有一些,哦,好吧
我建议看看这个style checker。
我发现了一个很好的 markdown 文件,几乎涵盖了 haskell 代码风格的各个方面。它可以用作备忘单。你可以在这里找到它:链接