【问题标题】:Good Haskell coding standards良好的 Haskell 编码标准
【发布时间】:2010-12-31 06:45:48
【问题描述】:

有人可以提供一个链接到一个好的 Haskell 编码标准吗?我找到了thisthis,但它们远非全面。更不用说 HaskellWiki 包含诸如“谨慎使用类”和“定义符号中缀标识符应该只留给图书馆作者”之类的“珍宝”。

【问题讨论】:

  • // ,这不是见仁见智吗?

标签: haskell coding-style conventions


【解决方案1】:

真的很难的问题。我希望你的回答能带来好的结果。同时,这里是我在初学者代码中发现的错误或其他烦人的目录。 Kornel Kisielewicz 指出的加州理工学院风格页面有一些重叠。我的一些建议就像 HaskellWiki “宝石”一样模糊和无用,但我希望它至少是更好的建议 :-)

  • 格式化您的代码,使其适合 80 列。 (高级用户可能更喜欢 87 或 88;除此之外正在推动它。)

  • 不要忘记let 绑定和where 子句创建了相互递归的定义嵌套,不是定义的序列。 p>

  • 利用where 子句,尤其是它们查看已经在范围内的函数参数的能力(很好的模糊建议)。如果你真的很喜欢 Haskell,你的代码应该比 let-bindings 有更多的where-bindings。太多的let-bindings 是未重构的 ML 程序员或 Lisp 程序员的标志。

  • 避免多余的括号。一些多余的括号特别令人反感的地方是

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

    • 围绕一个函数应用程序,它本身就是中缀运算符的参数(函数应用程序比任何中缀运算符绑定得更紧密。这个事实应该铭记在每个 Haskeller 的大脑中,大致相同我们恐龙的 APL 的从右到左扫描规则被烧毁的方式。)

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

  • 最好在函数与其参数之间留一个空格,即使参数是用括号括起来的。

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

    f $ g $ h x == (f . g . h) x == f . g . h $ x
    
  • 不要忽视内置的 MaybeEither 类型。

  • 永远不要写if <expression> then True else False;正确的短语是 <expression>

  • 当您可以使用模式匹配时,请勿使用 headtail

  • 不要忽视使用中缀点运算符的函数组合。

  • 谨慎使用换行符。换行可以提高可读性,但需要权衡:您的编辑器一次只能显示 40-50 行。如果您需要一次阅读和理解一个大型函数,则不能过度使用换行符。

  • 几乎总是更喜欢运行到行尾的-- cmets,而不是{- ... -} cmets。大括号 cmets 可能适用于大标题 - 就是这样。

  • 给每个顶级函数一个明确的类型签名。

  • 如果可能,对齐 -- 行、= 符号,甚至是出现在相邻行中的括号和逗号。

  • 1234563 /p>

【讨论】:

  • 我真的很喜欢这个答案,但是你能提供更多的代码示例吗?我仍然不完全熟悉 Haskell 术语,所以“函数应用程序比任何中缀运算符绑定得更紧密”和其他一些点让我感到困惑。
  • @CaptainCasey:我开始添加一些示例,但随后答案变得太长且难以阅读。这是一组简短的建议;如果要变成真正的风格指南,就必须由其他人来完成。但是让我知道你的具体观点。绑定紧密只是意味着(length l) + 1 很丑。 length 的应用程序自动绑定比+ 的应用程序更紧密,所以写的惯用的东西是length l + 1。括号是函数式程序的祸根。
  • about:Format your code so it fits in 80 columns. 我更喜欢 120 col 了.. 似乎没有什么适合 80。
  • 在阅读了您在 Haskell 上的大部分答案后,我可以肯定地说您是一位了不起的教授!
  • 您的风格建议很有帮助,但我仍然想知道您对带有许多奇怪运算符(如 =??、$$、 等许多 Haskeller 似乎喜欢的代码)的看法?这是好风格吗?对我来说,理解受影响的源代码非常困难。
【解决方案2】:

一些好的经验法则恕我直言:

  • 请咨询HLint,以确保您没有多余的大括号,并且您的代码不会毫无意义地指向完整。
  • 避免重新创建现有的库函数。 Hoogle 可以帮助您找到它们。
    • 很多时候,现有的库函数比要创建的函数更通用。例如,如果你想要Maybe (Maybe a) -> Maybe a,那么join 会这样做。
  • 参数命名和文档有时很重要。
    • 对于像 replicate :: Int -> a -> [a] 这样的函数,每个参数的作用非常明显,仅从它们的类型来看。
    • 对于采用多个相同类型参数的函数,例如 isPrefixOf :: (Eq a) => [a] -> [a] -> Bool,参数的命名/文档记录更为重要。
  • 如果一个函数的存在只是为了服务另一个函数,并且没有其他用途,和/或很难为它想一个好名字,那么它可能应该存在于它的调用者的 where 子句中,而不是在模块的范围。
  • 干燥
    • 在适当的时候使用 Template-Haskell。
    • zip3zipWith3zip4zipWith4 等功能非常多。使用 Applicative 样式和 ZipLists 代替。您可能永远不需要像这样的函数。
    • 自动派生实例。 derive 包可以帮助您派生类型类的实例,例如 Functor(只有一种正确的方法可以使类型成为 Functor 的实例)。
  • 更通用的代码有几个好处:
    • 它更有用且可重复使用。
    • 由于限制较多,因此不易出现错误。
      • 例如,如果您想对concat :: [[a]] -> [a] 进行编程,请注意它可以更通用为join :: Monad m => m (m a) -> m a。编程join 时出错的空间较小,因为在编程concat 时您可能会错误地反转列表,而在join 中您可以做的事情很少。
  • 当在您的代码中的许多地方使用相同的 monad 转换器堆栈时,请为其创建一个类型同义词。这将使类型更短、更简洁,并且更容易批量修改。
  • 谨防“惰性 IO”。例如,readFile 在读取文件时并没有真正读取文件的内容。
  • 避免缩进太多以至于我找不到代码。
  • 如果您的类型在逻辑上是类型类的实例,请将其设为实例。
    • 该实例可以用熟悉的功能替换您可能考虑过的其他接口功能。
    • 注意:如果有多个逻辑实例,请为这些实例创建新类型包装器。
    • 使不同的实例保持一致。如果列表 Applicative 的行为类似于 ZipList,那将会非常混乱/糟糕。

【讨论】:

    【解决方案3】:
    • 我喜欢尝试组织功能 作为无点风格的作品 尽可能地做事 喜欢:

      func = boo . boppity . bippity . snd
          where boo = ...
                boppity = ...
                bippity = ...
      
    • 我喜欢使用 ($) 来避免嵌套括号或长括号表达式

    • ...我以为我还有一些,哦,好吧

    【讨论】:

      【解决方案4】:

      我建议看看这个style checker

      【讨论】:

        【解决方案5】:

        我发现了一个很好的 markdown 文件,几乎涵盖了 haskell 代码风格的各个方面。它可以用作备忘单。你可以在这里找到它:link

        【讨论】:

          猜你喜欢
          • 2010-09-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-07-17
          • 2013-09-11
          • 1970-01-01
          • 2010-10-18
          相关资源
          最近更新 更多