初学者haskell代码在main中的do构造中出现类型错误

时间:2019-01-04 21:09:09

标签: haskell

我只是在学习一些简单的haskell代码,但是我一直关注的教程都没有提到尝试运行带有do和I / O类型的任何代码时继续遇到的错误。 GHCI似乎认为其某种类型错误。这是我的代码:

main = do
  putStrLn "This program can find all odd numbers between 1 and a desired number. Please enter a number."
  user <- getLine
  if (read user) < 1
      then
        do putStrLn "Improper input specified. 10 will be chosen as     default."
            putStrLn show oddsort 10 [] ++ "Is the list of odd numbers from 1 to 10."
    else
      do putStrLn show (oddsort (read user) [])

oddsort x xs
  |x == 1 = 1:xs
  |x `mod` 2 == 0 = oddsort (x-1) xs
  |otherwise = oddsort (x - 2) (x:xs)

3 个答案:

答案 0 :(得分:5)

您的代码中有两个错误:

  1. 缩进。始终确保do块中属于在一起的所有行都缩进同一级

    main = do
     ⋮putStrLn "This program..."
     ⋮user <- getLine
     ⋮if (read user) < 1
     ⋮   then
     ⋮      do⋮putStrLn "Improper..."
     ⋮        ⋮█putStrLn ...
     ⋮  else
     ⋮    do putStrLn ...
    

    请注意,在第三个putStrLn之前有一个空格。
    缩进对于许多其他Haskell构造(wherecaseclass)也很重要。 ..)。从样式上讲,我也会养成对齐else / then的习惯,尽管实际上标准并不需要。即

    main = do
     ⋮putStrLn "This program..."
     ⋮user <- getLine
     ⋮if (read user) < 1
     ⋮⋮then do
     ⋮⋮ ⋮putStrLn "Improper..."
     ⋮⋮ ⋮putStrLn ...
     ⋮⋮else do
     ⋮⋮ ⋮putStrLn ...
    
  2. 尽管putStrLn看起来像总是这样被解析的“行首的命令”,但事实并非如此-只是一个普通的函数。因此,putStrLn show oddsort 10 [] ++ "Bla"被解析为

      (putStrLn show oddsort 10 []) ++ "Bla"
    

    putStrLn应用于参数showoddsort10[]。显然,它不知道该怎么做,即这是一个巨大的类型错误!你真正想要的是

      putStrLn (show (oddsort 10 []) ++ "Bla")
    

    也可以用“括号化运算符” $编写:

      putStrLn $ show (oddsort 10 []) ++ "Bla"
    

答案 1 :(得分:2)

我将专注于这些行

do putStrLn "Improper input specified. 10 will be chosen as     default."
    putStrLn show oddsort 10 [] ++ "Is the list of odd numbers from 1 to 10."

问题:

  1. 缩进错误:块条目应完全在同一列上开始

  2. 第二行解析为(putStrLn show oddsort 10 []) ++ ("Is the list of odd numbers from 1 to 10."),这是错误的,因为您不能++putStrLn show oddsort 10 []这样的非列表。您需要括号。

  3. putStrLn show oddsort 10 []调用具有四个参数的函数putStrLnshowoddsort10[]。这不是您想要的,因此需要更多的括号。

可能还有更多问题,但这是最明显的问题。

一般建议:将类型签名添加到所有顶级绑定中,例如oddsort。这将帮助GHC产生更好的类型错误消息。 此外,使用-Wall打开警告也可以帮助检测某些问题。

答案 2 :(得分:1)

这是您代码的有效版本:

get

我对您的代码进行了一些更改,并按顺序列出了它们。

我做的第一件事是向oddsort :: Int -> [Int] -> [Int] oddsort x xs |x == 1 = x : xs |x `mod` 2 == 0 = oddsort (x - 1) (x:xs) |otherwise = oddsort (x - 2) (x:xs) main = do putStrLn "This program can find all odd numbers between 1 and a desired number. Please enter a number." user <- getLine if (read user :: Int) < 1 then putStrLn $ "Improper input specified. 10 will be chosen as default. \n" ++ ((show $ oddsort 10 []) ++ " Is the list of odd numbers from 1 to 10.") else putStrLn $ show $ (oddsort (read user :: Int) []) 添加类型签名,因为否则类型签名将留给编译器来推断(猜测)类型,而您实际上并不想使用函数来这样做,为什么? ?仔细检查this

然后我将oddsort更改为if (read user) < 1,因为您再次将其留给编译器来猜测if (read user :: Int) < 1的返回值,并在定义后读取返回的特定类型返回类型read的签名,它指定:: Int的返回类型是read

最后一个更改是在Int子句中,我只是删除了第二条then语句,并将putStrLn的结果连接到(show $ oddsort 10 []),因为有了"Is the list of odd numbers from 1 to 10.") GHC将show之后的所有内容作为参数,而putStrLn只能接受字符串,并且只能接受1个参数。您可以通过键入putStrLn show oddsort 10 []在repl上看到它。另外,我将它们串联为1个字符串,因为您实际上只需要1个,但是2个看起来更整洁,因此请保持这种方式,但要在第二个putStrLn中加上括号,这样就只有1个参数。您也可以使用:t show运算符。