良好的Haskell编码标准

Ale*_*nov 74 haskell coding-style conventions

有人可以为Haskell提供良好编码标准的链接吗?我发现了这个这个,但它们远非全面.更不用说HaskellWiki包含诸如"谨慎使用类"之类的"宝石","定义符号中缀标识符应仅留给图书馆作者".

Nor*_*sey 91

真的很难问.我希望你的答案好转.同时,这里是我在初学者代码中发现的错误或其他令人讨厌的事情的目录.与Kornel Kisielewicz指出的Cal Tech风格页面有一些重叠.我的一些建议与HaskellWiki"宝石"一样模糊和无用,但我希望至少它是更好的建议:-)

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

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

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

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

    • 围绕if表达的条件(品牌你作为一个未经重建的C程序员)

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

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

  • 首选函数及其参数之间的空格,即使参数是括号.

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

    f $ g $ h x == (f . g . h) x == f . g . h $ x
    
    Run Code Online (Sandbox Code Playgroud)
  • 不要忽视内置MaybeEither类型.

  • 从来不写if <expression> then True else False; 正确的短语很简单<expression>.

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

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

  • 仔细使用换行符.换行可以提高可读性,但需要权衡:您的编辑器一次只能显示40-50行.如果您需要同时阅读和理解大型功能,则不得过度使用换行符.

  • 几乎总是更喜欢在--评论中运行到行尾的{- ... -}注释.支撑的注释可能适用于大型标题 - 就是这样.

  • 为每个顶级函数提供显式类型签名.

  • 如果可能,请对齐相邻行中出现的--线条,=符号甚至括号和逗号.

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

  • 关于:`格式化你的代码,使它适合80列.我更喜欢120 col了......似乎没有任何东西适合80. (7认同)
  • 我真的很喜欢这个答案,但是你能提供更多的代码示例吗?我还没有完全熟悉Haskell的术语,因此"函数应用程序比任何中缀运算符更紧密",其他一些点让我感到困惑. (3认同)
  • @CaptainCasey:我开始添加一些例子,但答案太长了,难以阅读.这意味着一小段建议; 如果它变成一个真正的风格指南,它将不得不由其他人完成.但请告诉我你的具体观点.绑定紧张只意味着`(长度l)+ 1'是丑陋的.`length`的应用自动绑定比`+`的应用更紧密,所以写的惯用的东西是`length l + 1`.括号是功能程序的祸根. (2认同)

yai*_*chu 27

拇指imho的一些好的规则:

  • 咨询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,等都是非常MEH.使用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.


Kor*_*icz 6

我建议看一下这个样式检查器.


jbe*_*man 6

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

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

  • ......我以为我有更多的东西,哦,好吧