良好的Haskell编码标准


Answers:


93

真的很难。希望您的回答对您有所帮助。同时,这是我在初学者代码中发现的错误或其他令人讨厌的东西的目录。Kornel Kisielewicz指向的Cal Tech样式页面有一些重叠。我的一些建议像HaskellWiki的“宝石”一样含糊而无用,但我希望至少它是更好的建议:-)

  • 格式化您的代码,使其适合80列。(高级用户可能更喜欢87或88;除此之外,还可以继续使用。)

  • 不要忘了,let绑定和where条款创建一个定义相互递归窝,不是一个序列的定义。

  • 利用where子句,特别是子句查看范围内的函数参数的能力(含糊的建议)。如果您真的很讨厌Haskell,则代码中的-bindings应该where比-bindings多得多let。太多的let绑定是未重构的ML程序员或Lisp程序员的标志。

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

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

    • 围绕一个函数应用程序本身就是一个infix运算符的参数(Function应用程序比任何infix运算符都绑定得更紧密。这一事实应该被烧入每个Haskeller的大脑,就像我们的恐龙拥有APL从右到左的扫描规则一样烧了。)

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

  • 即使参数带有括号,也最好在函数及其参数之间留一个空格。

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

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

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

  • 不要使用headtail可以使用模式匹配时。

  • 不要用中缀点运算符忽略函数组成。

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

  • 几乎总是喜欢--在行尾添加{- ... -}注释的注释。大括号的注释可能适用于大型标题。

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

  • 尽可能对齐相邻行中出现的--行,=符号,甚至括号和逗号。

  • 受GHC中心的影响,我非常喜欢使用camelCase导出的标识符,并在short_name带下划线的本地where绑定或let绑定变量中使用下划线。


3
我真的很喜欢这个答案,但是您可以提供更多的代码示例吗?我仍然不完全了解Haskell的术语,因此“功能应用程序比任何infix运算符都更紧密地绑定”,还有其他几点使我感到困惑。
CaptainCasey'1

2
@CaptainCasey:我开始添加一些例子,但是答案太长了,很难阅读。这只是一些简短建议。如果要变成真正的样式指南,则必须由其他人来完成。但是,请让我知道您的具体观点。装订紧密度只是意味着(length l) + 1丑陋。应用length自动绑定比的应用程序更严格+,所以惯用的事,可以是length l + 1。括号是功能程序的祸根。
诺曼·拉姆齐

7
约:Format your code so it fits in 80 columns.我更喜欢120关口了..什么都没似乎适合在80
Talvi Watia

在阅读了Haskell的大部分答案后,我可以肯定地说您是一位了不起的教授!
fedvasu 2011年

您的样式建议很有帮助,但我仍然想知道您对许多Haskeller似乎喜欢的带有许多奇怪运算符(例如= ??,$$,<==>)的代码的看法?这是好风格吗?对我来说,这使得很难理解受影响的源代码。
mljrg 2015年

29

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

  • HLint咨询,以确保您没有多余的花括号,并且您的代码不是毫无意义的指向全部。
  • 避免重新创建现有的库函数。Hoogle可以帮助您找到它们。
    • 通常,现有的库函数比要编写的函数更通用。例如,如果您想要Maybe (Maybe a) -> Maybe a,则可以join执行其他操作。
  • 参数命名和文档有时很重要。
    • 对于像replicate :: Int -> a -> [a]这样的函数,很明显每个参数仅从它们的类型起就可以做什么。
    • 对于需要多个相同类型参数的函数,例如isPrefixOf :: (Eq a) => [a] -> [a] -> Bool,参数的命名/说明更为重要。
  • 如果一个功能仅存在于服务另一个功能,并且没有其他用处,和/或很难为它起一个好名字,那么它可能应该存在于调用者的where子句中,而不是模块的作用域中。
  • 干燥
    • 适当时使用Template-Haskell。
    • 功能捆绑喜欢zip3zipWith3zip4zipWith4,等都是非常MEH。改用sApplicative样式ZipList。您可能根本就不需要这些功能。
    • 自动派生实例。该派生包可以帮助您推导型课程,如情况下Functor(只有一个做一个类型的实例正确的方式Functor)。
  • 更通用的代码有几个好处:
    • 它更加有用和可重用。
    • 由于存在更多的约束,因此不容易出现错误。
      • 例如,如果您要编程concat :: [[a]] -> [a],请注意它如何变得更通用join :: Monad m => m (m a) -> m a。编程join时出错的空间较小,因为在编程时concat您可能会错误地反转列表,并且join您几乎无能为力。
  • 在代码中的许多地方使用同一堆monad转换器时,请为其输入类型同义词。这将使类型更短,更简洁并且更易于批量修改。
  • 当心“懒惰的IO”。例如,readFile在读取文件时并没有真正读取文件的内容。
  • 避免缩进太多,以至于我找不到代码。
  • 如果您的类型在逻辑上是类型类的实例,请使其成为实例。
    • 该实例可以用您熟悉的其他接口功能来代替。
    • 注意:如果有多个逻辑实例,请为这些实例创建newtype-wrappers。
    • 使不同的实例保持一致。如果该列表的Applicative行为类似于,那将是非常混乱/糟糕的ZipList

7
  • 我喜欢尝试通过以下方式将函数尽可能组织为无点样式组合:

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

  • ...我以为我里面还有更多,哦



4

我发现良好的markdown文件涵盖了Haskell代码样式的几乎每个方面。可以用作备忘单。您可以在这里找到它:链接

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.