良好的 Haskell 编码标准

发布于 2024-08-16 06:37:19 字数 281 浏览 6 评论 0原文

有人可以提供一个良好的 Haskell 编码标准的链接吗?我找到了这个这个,但它们远非全面。更不用说 HaskellWiki 中包含了诸如“小心使用类”和“定义符号中缀标识符应该只留给库编写者”这样的“宝石”。

Could someone provide a link to a good coding standard for Haskell? I've found this and this, but they are far from comprehensive. Not to mention that the HaskellWiki one includes such "gems" as "use classes with care" and "defining symbolic infix identifiers should be left to library writers only."

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

妄断弥空 2024-08-23 06:37:19

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

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

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

  • 利用 where 子句,尤其是它们查看已在范围内的函数参数的能力(很好的模糊建议)。如果您真正熟悉 Haskell,那么您的代码中的 where 绑定应该比 let 绑定多得多。太多 let 绑定是未重构的 ML 程序员或 Lisp 程序员的标志。

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

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

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

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

  • 首选函数及其参数之间有空格,即使参数带括号。

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

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

  • 永远不要写if <表达式>;然后为 True,否则为 False;正确的短语是<表达式>

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

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

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

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

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

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

  • 受 GHC 中心的影响,我非常倾向于使用 camelCase 来表示导出的标识符,使用 short_name 来表示本地 where 并使用下划线>-bound 或 let-bound 变量。

Really hard question. I hope your answers turn up something good. Meanwhile, here is a catalog of mistakes or other annoying things that I have found in beginners' code. There is some overlap with the Cal Tech style page that Kornel Kisielewicz points to. Some of my advice is every bit as vague and useless as the HaskellWiki "gems", but I hope at least it is better advice :-)

  • Format your code so it fits in 80 columns. (Advanced users may prefer 87 or 88; beyond that is pushing it.)

  • Don't forget that let bindings and where clauses create a mutually recursive nest of definitions, not a sequence of definitions.

  • Take advantage of where clauses, especially their ability to see function parameters that are already in scope (nice vague advice). If you are really grokking Haskell, your code should have a lot more where-bindings than let-bindings. Too many let-bindings is a sign of an unreconstructed ML programmer or Lisp programmer.

  • Avoid redundant parentheses. Some places where redundant parentheses are particularly offensive are

    • Around the condition in an if expression (brands you as an unreconstructed C programmer)

    • Around a function application which is itself the argument of an infix operator (Function application binds tighter than any infix operator. This fact should be burned into every Haskeller's brain, in much the same way that us dinosaurs had APL's right-to-left scan rule burned in.)

  • Put spaces around infix operators. Put a space following each comma in a tuple literal.

  • Prefer a space between a function and its argument, even if the argument is parenthesized.

  • Use the $ operator judiciously to cut down on parentheses. Be aware of the close relationship between $ and infix .:

    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.

迷爱 2024-08-23 06:37:19

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

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

Some good rules of thumbs imho:

  • Consult with HLint to make sure you don't have redundant braces and that your code isn't pointlessly point-full.
  • Avoid recreating existing library functions. Hoogle can help you find them.
    • Often times existing library functions are more general than what one was going to make. For example if you want Maybe (Maybe a) -> Maybe a, then join does that among other things.
  • Argument naming and documentation is important sometimes.
    • For a function like replicate :: Int -> a -> [a], it's pretty obvious what each of the arguments does, from their types alone.
    • For a function that takes several arguments of the same type, like isPrefixOf :: (Eq a) => [a] -> [a] -> Bool, naming/documentation of arguments is more important.
  • If one function exists only to serve another function, and isn't otherwise useful, and/or it's hard to think of a good name for it, then it probably should exist in it's caller's where clause instead of in the module's scope.
  • DRY
    • Use Template-Haskell when appropriate.
    • Bundles of functions like zip3, zipWith3, zip4, zipWith4, etc are very meh. Use Applicative style with ZipLists instead. You probably never really need functions like those.
    • Derive instances automatically. The derive package can help you derive instances for type-classes such as Functor (there is only one correct way to make a type an instance of Functor).
  • Code that is more general has several benefits:
    • It's more useful and reusable.
    • It is less prone to bugs because there are more constraints.
      • For example if you want to program concat :: [[a]] -> [a], and notice how it can be more general as join :: Monad m => m (m a) -> m a. There is less room for error when programming join because when programming concat you can reverse the lists by mistake and in join there are very few things you can do.
  • When using the same stack of monad transformers in many places in your code, make a type synonym for it. This will make the types shorter, more concise, and easier to modify in bulk.
  • Beware of "lazy IO". For example readFile doesn't really read the file's contents at the moment the file is read.
  • Avoid indenting so much that I can't find the code.
  • If your type is logically an instance of a type-class, make it an instance.
    • The instance can replace other interface functions you may have considered with familiar ones.
    • Note: If there is more than one logical instance, create newtype-wrappers for the instances.
    • Make the different instances consistent. It would have been very confusing/bad if the list Applicative behaved like ZipList.
蓝戈者 2024-08-23 06:37:19
  • 我喜欢尝试组织功能
    作为无点风格的作品
    尽可能地做事
    像:

    func = boo 。波皮蒂。比皮蒂。斯德
        其中嘘= ...
              波皮蒂 = ...
              双皮蒂 = ...
    
  • 我喜欢使用 ($) 只是为了避免嵌套括号或长括号表达式

  • ...我以为我有一个我身上还有更多,哦,好吧

  • I like to try to organize functions
    as point-free style compositions as
    much as possible by doing things
    like:

    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

-残月青衣踏尘吟 2024-08-23 06:37:19

我建议看看这个样式检查器。

I'd suggest taking a look at this style checker.

海拔太高太耀眼 2024-08-23 06:37:19

我发现很好的 Markdown 文件几乎涵盖了 Haskell 代码风格的各个方面。它可以用作备忘单。您可以在这里找到它:链接

I found good markdown file covering almost every aspect of haskell code style. It can be used as cheat sheet. You can find it here: link

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文