5

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

第一 - 漂亮:

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

这种风格看起来很棒,但很脆弱:让我们重构这段代码并重命名funcA——我们需要重新缩进then.elsedo. 重命名n. 这很烦人。非常。

编辑正如 FUZxxl 提到的,行变得越来越长,但其中大多数 - 开头的空格。

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

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

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

4

2 回答 2

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

如果您可以将列表中的第一个放在关键字之后,我只会在使用 pretty 样式时这样做

 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

然而,在本例中,我将重构代码以使用模式匹配和显式一元操作(在本例中>>),替换do. 如果它do很短,请不要这样做。

funcA :: Integer -> IO ()
funcA 0 = putStrLn "zero"
funcA n = putStr "--> " >> print n
于 2012-06-25T19:20:21.343 回答
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

elseanddo折叠到同一行(尽管在这种情况下,我在 之后开始新行do)。类似地,整个定义为do块的函数通常在函数头中do紧跟在后面,块的实际代码在缩进的一组行中。=do

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

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

不过,我很确定这种 if/then/else 格式取决于 GHC 中相对较新的变化;以前thenandelse部分必须比 . 缩进更多if。在这条规则下,我从来没有找到一种我非常熟悉的 if/then/else 块的编写方式;幸运的是,由于模式匹配和守卫,它们在 Haskell 中并不常见。

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

foo a b = simple expression c d
    where
        c = bar a
        d = baz b

看起来不错,但是:

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

逻辑上where应该与函数主体处于不同的缩进级别。但它不应该缩进更多,因为它是 的一部分foo a b =,而不是 的一部分complex ...。但是我也不想再缩进complex ...一级来回应,因为跳转到两级缩进看起来很难看,而且它的正确缩进级别由后面是否有where块来确定是令人不快的。where幸运的是,当函数的定义是一些辅助定义的简单表达时,我似乎大多使用;如果该函数作为一些辅助定义的大型复杂函数突然出现,我会尝试将其分解为更独立的部分。

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

我有时担心我的思维方式太像 Pythonista(具有缩进级别)或 Mercuryista(?)(具有具有节的代码结构),而不是采用 Haskell 的“缩进比开始的位置更多”方法。但是一旦事情开始变得复杂,像你的第一个漂亮的例子这样的东西就变得非常不漂亮,而且我的口味不可读。

于 2012-06-27T02:26:03.137 回答