良好的 Haskell 编码标准

有人能为 Haskell 提供一个好的编码标准的链接吗?我已经找到了 这个这个,但它们还远远不够全面。更不用说 HaskellWiki 包含了这样的“ gems”,比如“谨慎使用类”和“定义符号中缀标识符应该只留给库编写者”

11558 次浏览

我建议你看看这个 风格检查员

这个问题很难回答。希望你的回答能带来好消息。与此同时,这里是我在初学者代码中发现的错误或其他恼人的东西的目录。科内尔 · 基谢莱维奇指向的加州理工学院风格页面有一些重叠之处。我的一些建议和 HaskellWiki 的“宝石”一样模糊和无用,但我希望它至少是更好的建议: -)

  • 设置代码的格式,使其适合80列。(高级用户可能更喜欢87或88; 超过这个数字就是在推动它。)

  • 不要忘记,let绑定和 where子句创建了一个相互递归的定义巢,没有序列都是定义。

  • 利用 where子句,特别是它们查看已经在作用域中的函数参数的能力(很好的模糊建议)。如果您真的在使用 Haskell,那么您的代码应该比 let绑定拥有更多的 where绑定。过多的 let绑定是不重构 ML 程序员或 Lisp 程序员的标志。

  • 避免使用多余的括号。有些地方多余的括号特别令人讨厌

    • 围绕 if表达式中的条件(将您标记为未重构的 C 程序员)

    • 围绕函数应用程序,函数应用程序本身就是中缀操作符(函数应用程序绑定比任何中缀操作符都要严格。这个事实应该深入每个 Haskeller 的大脑,就像我们恐龙的 APL 从右到左的扫描规则一样。)

  • 在中缀运算符周围放置空格。在元组文字中每个逗号后面放置一个空格。

  • 在函数和它的参数之间选择一个空格,即使参数是括号。

  • 明智地使用 $操作符来减少括号。注意 $和中缀 .之间的密切关系:

    f $ g $ h x == (f . g . h) x == f . g . h $ x
    
  • Don't overlook the built-in Maybe and Either types.

  • Never write if <expression> then True else False; the correct phrase is simply <expression>.

  • Don't use head or tail when you could use pattern matching.

  • Don't overlook function composition with the infix dot operator.

  • Use line breaks carefully. Line breaks can increase readability, but there is a tradeoff: Your editor may display only 40–50 lines at once. If you need to read and understand a large function all at once, you mustn't overuse line breaks.

  • Almost always prefer the -- comments which run to end of line over the {- ... -} comments. The braced comments may be appropriate for large headers—that's it.

  • Give each top-level function an explicit type signature.

  • When possible, align -- lines, = signs, and even parentheses and commas that occur in adjacent lines.

  • Influenced as I am by GHC central, I have a very mild preference to use camelCase for exported identifiers and short_name with underscores for local where-bound or let-bound variables.

  • 我喜欢组织活动 作为无点风格的作品 尽可能多地做事 例如:

    func = boo . boppity . bippity . snd
    where boo = ...
    boppity = ...
    bippity = ...
    
  • I like using ($) only to avoid nested parens or long parenthesized expressions

  • ... I thought I had a few more in me, oh well

一些好的拇指法则:

  • 请咨询 提示,以确保您没有多余的大括号,并且您的代码不是无意义的满点。
  • 避免重新创建现有的库函数。 胡格尔可以帮助您找到它们。
    • 通常现有的库函数比将要创建的函数更一般。例如,如果您想要 Maybe (Maybe a) -> Maybe a,那么 join除了做其他事情之外还做这些事情。
  • 参数命名和文档有时很重要。
    • 对于像 replicate :: Int -> a -> [a]这样的函数,每个参数的作用非常明显,仅从它们的类型来看。
    • 对于采用同一类型的多个参数(如 isPrefixOf :: (Eq a) => [a] -> [a] -> Bool)的函数,参数的命名/文档化更为重要。
  • 如果一个函数的存在只是为了服务于另一个函数,并且在其他方面没有用处,或者很难为它想出一个好的名字,那么它可能应该存在于调用者的 where子句中,而不是模块的作用域中。
  • 干的
    • 适当时使用 Template-Haskell。
    • zip3zipWith3zip4zipWith4这样的函数束非常少。使用 Applicative样式与 ZipList代替。您可能永远不会真正需要这样的函数。
    • 自动派生实例。推导包可以帮助您派生类型类的实例,例如 Functor(只有一种正确的方法可以使类型成为 Functor的实例)。
  • 更一般的代码有几个好处:
    • 它更有用,更可重复使用。
    • 它不太容易出错,因为有更多的约束。
      • 例如,如果您想编写 concat :: [[a]] -> [a]程序,请注意它可以更一般化为 join :: Monad m => m (m a) -> m a。在编写 join时,出错的空间较小,因为在编写 concat时,你可以错误地反转列表,而在 join中,你能做的事情很少。
  • 当在代码的许多地方使用相同的单子变换器堆栈时,为它创建一个类型同义词。这将使类型更短、更简洁,并且更容易批量修改。
  • 小心“惰性 IO”。例如,readFile在读取文件时并不真正读取文件的内容。
  • 避免缩进太多,以至于我找不到代码。
  • 如果类型在逻辑上是类型类的实例,则将其设置为实例。
    • 该实例可以替换您可能已经考虑过的其他接口函数。
    • 注意: 如果有多个逻辑实例,则为实例创建 newtype-wrappers。
    • 使不同的实例保持一致。如果列表 Applicative的行为类似于 ZipList,那将是非常混乱/糟糕的。

我发现好的降价文件几乎涵盖了 haskell 代码风格的所有方面。它可以用作备忘单。你可以在这里找到它: 链接