Haskell代码格式化

时间:2012-06-25 19:11:30

标签: haskell coding-style

Haskell基于缩进的块样式。我知道两种风格,但我无法确定哪种风格更好。 (原谅我一个非常愚蠢的例子函数)

1st - 漂亮:

funcA :: Integer -> IO ()
funcA n = if n == 0
            then putStrLn "zero"
            else do putStr "--> "
                    print n

这种风格看起来很棒,但它非常脆弱:让我们重构这段代码并重命名funcA - 我们需要重新加注thenelse以及{{1}中的所有表达式}。对于do的重命名也是如此。这很烦人。非常

编辑正如FUZxxl所提到的那样,线条变得越来越长,但大多数线条在开始时都是空格。

另一种风格是重构友好,但不那么漂亮:

n

您喜欢什么样的风格?为什么?可能你有另一个或你有一些链接到代码风格的一些伟大的开发人员解释?

2 个答案:

答案 0 :(得分:5)

就个人而言,我使用两种风格的混合。

当下一个"漂亮"缩进层不是太远了,我用的是第一个样式

do foo
   bar
   baz

但是当一个漂亮的缩进会导致代码在右侧太远时,我只使用两个空格

case foo of
  a -> bar
  b -> baz
  c -> quux

当我的缩进比例超过20个空格时,我常常会重置"缩进级别:

do a
   b <- do c -- normally I use only two whitespaces here
           d
           e <- do
     f
     g
     h

如果您可以在关键字之后放置第一个事物列表,我只会在使用漂亮风格时执行此操作

 main = do x
           y
           z where
   a = b
   c = d
   e = f

你的例子

funcA :: Integer -> IO ()
funcA n = if n == 0
  then putStrLn "zero"
  else do putStr "--> "
          print n

但是,在这个例子中,我将重构代码以使用模式匹配和显式monadic操作(在本例中为>>),替换do。如果do很短,请不要这样做。

funcA :: Integer -> IO ()
funcA 0 = putStrLn "zero"
funcA n = putStr "--> " >> print n

答案 1 :(得分:1)

我还没有想出我的“理想”Haskell风格。我倾向于编写代码的方式可能受Python和Mercury编码的影响最大。

我倾向于喜欢多线结构有一个明显的“标题”线,它告诉我没有仔细阅读的结构是什么(这通常意味着多线结构的“形状”应该由非常确定“标题”行的开始或结束,并通过改变缩进清楚地描绘具有多个不同多行部分的多行结构的不同“组成部分”。所以我更有可能把你的例子写成:

funcA :: Integer -> IO ()
funcA n =
    if n == 0 then
        putStrLn "zero"
    else
        do  putStr "--> "
            print n

或者可能:

funcA :: Integer -> IO ()
funcA n =
    if n == 0 then
        putStrLn "zero"
    else do
        putStr "--> "
        print n

elsedo折叠到同一行(尽管在这种情况下我会在do之后开始一个新行)。类似地,整个定义为do块的函数通常紧跟在函数头中的do之后的=,以及紧跟在do块之后的funcA :: Integer -> IO () funcA n = if n == 0 then putStrLn "zero" else do putStr "--> " print n 块的实际代码。缩进的行集。

如果if / then / else的条件更复杂,我也会给它自己的“部分”,成为:

then

我很确定if / then / else的格式取决于GHC相对较新的变化;以前,elseif部分必须缩进超过foo a b = simple expression c d where c = bar a d = baz b 。我从来没有找到一种写作方式if / then / else阻止我在这条规则下非常舒服;幸运的是,由于模式匹配和守卫,它们在Haskell中并不常见。

我使用这种风格的方式有些不一致,我还不满意。例如,在我目前已打开的文件中,我有以下几种形式的函数:

foo a b =
    complex multiline expression c d
    where
        c = bar a
        d = baz b

看起来不错,但随后:

where

foo a b =在逻辑上应该与函数主体的缩进级别不同。但它不应该更多缩进,因为它是complex ...的一部分,而不是complex ...的一部分。但我也不想通过缩进where一个级别来回应,因为跳转到两个缩进级别看起来很难看,并且由于是否存在{{1}而确定其正确的缩进级别是令人不愉快的。之后阻止。幸运的是,当函数的定义是一些辅助定义的简单表达时,我似乎主要使用where;如果函数作为一些辅助定义的大型复杂函数而引起关注,我会尝试将其分解为更独立的部分。

我觉得这种风格大多避免了失控的缩进(它也是“重构友好”而不是通过其他代码的长度来确定代码的正确缩进位置),同时仍允许我直观地确定高级结构我的代码,而不必对低级结构进行详细的解析。

我有时担心我的想法太像Pythonista(带缩进级别)或Mercuryista(?)(带有部分的代码结构),而不是采用Haskell的“缩进的东西而不是位置他们开始“接近。但是一旦事情开始变得复杂,像你的第一个漂亮的例子就会变得毫无疑问,并且根本不可读。