Haskell “哈斯克尔”;其中;缩进:为什么必须缩进超过标识符?

Haskell “哈斯克尔”;其中;缩进:为什么必须缩进超过标识符?,haskell,indentation,Haskell,Indentation,此代码: import Data.Char (digitToInt) myInt :: String -> Int myInt [] = error "bad input: empty string" myInt (x:xs) | x == '-' = -1 * myInt xs | otherwise = foldl convert 0 (x:xs) where convert acc x | x `elem` ['0'..'9'] = 10 * acc

此代码:

import Data.Char (digitToInt)

myInt :: String -> Int
myInt [] = error "bad input: empty string"
myInt (x:xs)
  | x == '-'  = -1 * myInt xs
  | otherwise = foldl convert 0 (x:xs)
  where convert acc x
        | x `elem` ['0'..'9'] = 10 * acc + digitToInt x
        | otherwise           = error ("bad input: not an int - " ++ [x])
失败:

前奏曲>:l safeListFs.hs
[1/1]编译Main(safeListFs.hs,已解释)

safeListFs.hs:9:8:解析错误(可能缩进不正确)
失败,已加载模块:无

但这个版本:

import Data.Char (digitToInt)

myInt :: String -> Int
myInt [] = error "bad input: empty string"
myInt (x:xs)
  | x == '-'  = -1 * myInt xs
  | otherwise = foldl convert 0 (x:xs)
  where convert acc x
          | x `elem` ['0'..'9'] = 10 * acc + digitToInt x
          | otherwise           = error ("bad input: not an int - " ++ [x])
好的:

前奏曲>:l safeListFs.hs
[1/1]编译Main(safeListFs.hs,已解释)
好的,模块已加载:Main


我不明白为什么最后两个缩进很重要。

因为您应该始终缩进函数定义。 (在您的例子中,所有内容都从
中的同一列开始,其中
被视为“同一级别”定义)

嵌套上下文必须比封闭上下文进一步缩进(n>m)。否则,L将失败,编译器应指示布局错误

这也会失败:

import Data.Char (digitToInt)

myInt :: String -> Int
myInt [] = error "bad input: empty string"
myInt (x:xs)
| x == '-'  = -1 * myInt xs
| otherwise = foldl convert 0 (x:xs)
where convert acc x
        | x `elem` ['0'..'9'] = 10 * acc + digitToInt x
        | otherwise           = error ("bad input: not an int - " ++ [x])
呃,我不擅长解释事情。在
where
关键字之后有一个新的上下文,因为您可以在其中指定多个函数--请记住,您的程序以implicit
module Main where
开头,因此我认为要求函数体缩进是合乎逻辑的,就像在模块级别上一样(编译器希望M列和N列上有另一个标识符,声明体将进一步缩进)


基本上,Haskell注意到
where
之后第一个非空格字符出现的列(在本例中,是
convert
c
),并将该列中开始的以下行视为
where
中的新定义

延续前一行定义的行(如
|
保护)必须缩进到第一个非空格字符的右侧(
c

c
左侧缩进的一行将位于
where
之外(例如,下一个顶级函数的开始)

这是
后面第一个字符的列,其中
非常重要,即使它位于新行:

  where
    convert acc x
      | ...
    anotherFunction x y

    ^ 

这个问题很好地说明了为什么我讨厌Haskell的空白语法;与Python相比,它总是让我觉得不直观。不幸的是,我最不喜欢的是代码中乱七八糟的难看的花括号。越位规则对我来说似乎很直观。你只需停止思考块(比如Python,这在Haskell中没有意义)考虑声明或表达式的连续性。使用一个聪明的文本编辑器,忘记奇怪的识别规则。我使用vim.With haskell缩进。但是,它在这种情况下没有帮助。离题:我有一个朋友,他的名字是Andrei PoluektovI,他仍然不明白这个答案,似乎在两个例子中,都在t下面他用烟斗(“|”)吹了一声,然后把管子缩进了凹槽same@Evan,“where”在这两种情况下都正确缩进。“where”之后的管道会产生错误。因为它们没有针对“convert”正确缩进(在第一个示例中)虽然你的答案显然是正确的,但这并不是针对我的特别困惑。这是基于嵌套范围缩进将从“where”开始的假设。而实际上它是从函数名开始的。我仍然不明白这一点:说说为什么OP的示例可以工作,为什么不工作,为什么不工作的示例不能工作。我仍然不确定我是否理解这一点:函数在模块范围下,缩进为0,但在模块范围下的位置缩进为1?++太好了,我无法理解其他答案,这很像@arternave最近在评论中建议的,我认为这解释了问题。
  where
    convert acc x
      | ...
    anotherFunction x y

    ^