让block给出缩进错误

时间:2017-12-18 19:01:29

标签: haskell

我知道缩进错误是什么,但我不知道为什么我在这里得到这个错误,而每个都是对齐的,试图解决它2个小时。

Account.hs:40:25: error:
    parse error (possibly incorrect indentation or mismatched brackets)
   |
40 |                         let amount = readLn :: IO Int
   |                         ^
Failed, 0 modules loaded.
main = do
            putStrLn $ "Press one to create a new account"
            let g = getLine
                enteredValue = read g :: Int
            if g == 1
                then do putStrLn $ "Enter your name "
                        let name = getLine
                            putStrLn $ "Enter the initial amount"
                            let amount = readLn :: IO Int
                                value  = Account (name,1,amount) Saving
                                show value
                else do putStrLn $ "Nothing"

我也试过这个版本,但这也给了incorrect indentation or mismatched brackets

main = do
        putStrLn $ "Press one to create a new account"
        let g = getLine
            enteredValue = read g :: Int
        if g == 1
            then do putStrLn $ "Enter your name "
                    let name = getLine
                        putStrLn $ "Enter the initial amount"
                        amount = readLn :: IO Int
                        value  = Account (name,1,amount) Saving
                        show value
            else do putStrLn $ "Nothing"

3 个答案:

答案 0 :(得分:8)

问题在于:

--                  |<---- "column 0" of this 'do' block 
            then do putStrLn $ "Enter your name "
--                  | still good; a 'let' statement:
                    let name = getLine
--                      |<---- "column 0" of this 'let' block
                        putStrLn $ "Enter the initial amount"
--                      | Huh, there's no '=' in ^this^ declaration?
                        let amount = readLn :: IO Int
--                      ^^^ Why is there a 'let' within another let binding?
--                          I still haven't seen a '='. Better throw a parse error.

基本上,putStrLn $ "Enter the initial amount"与前一行中的name = ...对齐,因此编译器将其作为声明(同一let块的一部分)读取。

要修复缩进错误,应该是:

main = do
            putStrLn $ "Press one to create a new account"
            let g = getLine
                enteredValue = read g :: Int
            if g == 1
                then do putStrLn $ "Enter your name "
                        let name = getLine
                        putStrLn $ "Enter the initial amount"
                        let amount = readLn :: IO Int
                            value  = Account (name,1,amount) Saving
                        show value
                else do putStrLn $ "Nothing"

但是你会遇到类型错误:

  • read g错误:readStringg :: IO String
  • g == 1错误:1Int,但是g :: IO String
  • show value错误:show返回String,但您将其用作IO操作
  • 您尚未展示Account的声明,但您可能也会遇到nameamount的问题

您可能需要以下内容:

main = do
            putStrLn $ "Press one to create a new account"
            g <- getLine
            let enteredValue = read g :: Int
            if enteredValue == 1
                then do putStrLn $ "Enter your name "
                        name <- getLine
                        putStrLn $ "Enter the initial amount"
                        amount <- readLn :: IO Int
                        let value  = Account (name,1,amount) Saving
                        putStrLn (show value)
                else do putStrLn $ "Nothing"

基本上,使用v <- exprexpr :: IO Something转到v :: Something

其他说明:

  • g <- getLine; let enteredValue = read g :: Int更好地写为enteredValue <- readLn :: IO Int
  • putStrLn (show value)可缩短为print value
  • 单个表达式不需要do(单个操作数也不需要$):... else putStrLn "Nothing"

答案 1 :(得分:4)

您的代码错误而不仅仅是Indentation Errors - 所以我的第一个建议是阅读learn you a haskell for great good

接下来haskell中有两个赋值运算符 - 一个 bind 是一个动作… <- …的结果,另一个是纯计算的本地定义/声明{{1} }。

此外,您可以通过将let … = …替换为read来考虑可能的错误输入(有意和无意地),从而改善read值。后者返回readMaybe,例如Maybe somethingreadMaybe "1" = Just 1 :: Maybe Int

关于缩进,最好将一个解决方案与您自己的程序进行比较:

readMaybe "foo" = Nothing :: Maybe Int
  

此处import Text.Read (readMaybe) data Type = Saving | Checking deriving (Show) data Account = Account (String,Int,Int) Type deriving (Show) main :: IO () main = do putStrLn "Press one to create a new account" g <- getLine let enteredValue = readMaybe g :: Maybe Int 的结果和输入的值具有相同的范围,因此它们具有相同的缩进 - 我们仅在getLine - 块的下一个if之后更改范围 - then - 块不共享每个分支的“声明”,因此您无法在else - 块中使用name,但可以使用else两者。

enteredValue
  

此处再次 if enteredValue == Just 1 then do putStrLn "Enter your name " name <- getLine putStrLn "Enter the initial amount" amount' <- fmap readMaybe getLine name共享相同的范围,amount'上的模式匹配会创建一个新的范围,其中amount'可见且匹配{{1}你无法使用这个变量。

amount

答案 2 :(得分:4)

let用于绑定值,以let x = y+z形式完成,其中x是绑定的名称(也称为“标识符”),y+z是它被绑定的表达式。

在您的示例中,我看到三个绑定:nameamountvalue。其余的不是值绑定,而是操作

do表示法中,操作不需要let。你只是一个接一个地写它们。所以:

let name = getLine
putStrLn $ "Enter the initial amount"
let amount = readLn :: IO Int
let value  = Account (name,1,amount) Saving
show value

但是等等!这不是全部!

getLine实际上并不是String类型的表达式,因为您似乎希望在此处。相反,getLine操作。为了使其“运行”并“生成”String值,您需要使用<-结构而不是let

name <- getLine

readLn类似:

amount <- readLn :: IO Int

最后,show value实际上并不是将值打印到屏幕的操作。 show是一个获取值并返回String的函数。它不会“做”任何事情(即不产生任何外部效果),因此您无法使用它代替do符号中的动作。如果您想要一个操作,它会在屏幕上打印一个值,那就是print

print value

收集所有内容:

name <- getLine
putStrLn $ "Enter the initial amount"
amount <- readLn :: IO Int
let value = Account (name,1,amount) Saving
print value

在解决所有这些问题之后,您在计划的第一部分遇到类似的困难,即let g = getLine而不是g <- getLine