【问题标题】:Simple haskell code can't compile简单的haskell代码无法编译
【发布时间】:2014-04-15 00:48:01
【问题描述】:

我开始学习 Haskell,只是尝试了一个简单的代码,运行时它显示了一些错误

doubleMe :: Int -> Int
doubleMe x = x + x

main = do
    doubleMe 2

ghc -c first.hs

错误是:

$ ghc -c first.hs

first.hs:4:1: 无法匹配预期类型 IO t0' with actual typeInt' 在表达式中:主要 检查函数类型时 `main'

虽然我使用 GCHi 进行调试,但先加载 doubleMe 函数并稍后调用它没有问题。任何帮助将不胜感激。

【问题讨论】:

  • 我建议使用 ghci(交互式环境),这样您就不必为简单的手动测试编译代码。您可以只输入:l first.hs 来加载您的定义,然后输入doubleMe 2,而无需繁琐的edit-main-every-time-I-want-to-check-something faf。 (有关如何编写main 的建议,请参阅答案。)
  • 哇...非常感谢所有这么快回答我问题的人。 Haskell 社区对新手的友好态度给我留下了深刻的印象。每个人的 cmets 都可以被接受为答案,但由于只能接受一个答案,我选择 @tanmaig 的,因为他有最彻底的解释。再次感谢大家!

标签: haskell


【解决方案1】:

Haskell 中的每个函数都返回一个特定类型的值。

在这种情况下,doubleMe 函数返回一个 Int 类型的值。当您从 ghci 解释器加载文件时,它会加载文件中的所有函数并使它们可供您使用。

main 函数是一个稍微特殊的函数,与其他语言很像。它定义了 Haskell 程序的入口点。然而,由于它只是另一个 Haskell 函数,它也必须返回一个特定类型的值。 main 函数被限制为返回 IO 类型的值,最常见的是 IO ()。

IO 本身也很特别,属于一类叫做 monads 的类型。你可以阅读IO monad here.

现在让我们回到您的代码:

main = do
    doubleMe 2

暂时忽略do 语法。您基本上是在告诉编译器main 函数返回doubleMe 2,它的类型为Int。这不会飞。 main 已经返回一个 IO 类型的值。在这种情况下,您可以使用return 函数将Int 值转换为IO Int 值。 (return 函数是所有 monadic 类型必须具有的函数。很简单地说,它将任何类型的值转换为 monadic 值。)

这样就变成了:

main = do
    return (doubleMe 2)

这是完全有效的代码,并且可以编译。然而,一旦你运行这个程序,你会发现它什么也没做。这是因为程序返回值 4。 其实不用do也可以写,就变成了:

main = return (doubleMe 2)

这也行。

但是,让我们假设您要打印出该值。这就是IO 真正发挥作用的地方。打印到屏幕是IO 操作。

main = do
          print (doubleMe 2)
          return (doubleMe 2)

do 表达式允许您链接一组IO 操作。所以你的程序仍然会返回值4,但它会首先计算表达式print (doubleMe 2)。正如预期的那样,这实际上会导致打印值doubleMe 2。 检查 ghci 中的 print 函数。

> :t print
print :: Show a => a -> IO ()

打印函数作用于可以显示的任何类型的值,它会导致IO 操作(打印到屏幕)但不返回任何()。

所有这些示例都有效,希望能让事情变得更清楚。 (查看main 的类型签名)。

-- main :: IO Int
main = return (doubleMe 2)

-- main :: IO Int
main = do 
          print (doubleMe 2)
          return (doubleMe 2)

-- main :: IO ()
main = do
          print (doubleMe 2)
          return ()

-- main :: IO ()
main = do
          print (doubleMe 2)

-- main :: IO ()
main = print (doubleMe 2)

-- main :: IO ()
main = do
         print "Hello"
         print (doubleMe 2)
         return ()

-- main :: IO String
main = do
         print "Hello"
         print (doubleMe 2)
         return "Hello"

【讨论】:

    【解决方案2】:

    main 必须是 IO a 类型的值,并且 doubleMe 2 具有 Int 类型。你可能想print这个值:

    main = print (doubleMe 2)
    

    【讨论】:

      【解决方案3】:

      它不起作用,因为任何使用 do 表示法的东西都必须返回一个 monad。 GHCI 做了一些事情来使它更有用,但它并不完全像 GHC 编译的代码那样工作。

      print :: Show a => a -> IO ()

      print 接受一个作为Show 实例的值并返回一个空的IO 操作。这很可能是您想要做的。

      main = print (doubleMe 2)

      应该像你想做的那样工作。您没有定义main 函数的类型,但在haskell 中必须具有main :: IO () 类型。查看doubleMe 的类型,您可能会明白为什么您的代码不起作用。

      【讨论】:

      • 请不要说“return a monad”之类的话。这是草率的措辞,鼓励草率的思考。作为 monad 是类型构造函数的谓词。函数返回值,而不是类型。保持类型和值级别分开对于理解 Haskell 至关重要,在教初学者时我们应该特别小心。说“main 必须是 IO 值,而你的表达式不是”之类的话。清晰准确的沟通真的很重要。
      • 更不用说,do 块必须产生一元类型的值也是完全错误的。 do () 完全有效。
      【解决方案4】:

      您的main 函数不接受任何内容并返回一个整数。你可能想要

      doubleMe :: Int -> Int
      doubleMe x = x + x
      
      main = do
          print $ doubleMe 2
      

      而是将 4 输出到 STDOUT。

      恭喜,您遇到了Monads。 main 是一个 IO monad,必须这样做。 IO monad 不返回 Ints。相反,IO Monad 是一个 IO 操作,由 print 返回并在运行时执行。

      【讨论】:

        【解决方案5】:

        幕后发生了一些事情,使您难以理解此错误消息。原因如下:

        1. 您的文件没有模块名称,因此 ghc 假定您正在定义模块 Main。
        2. 模块Main 中的名称main 必须具有IO something 类型。
        3. 但是你写的main的类型是Int。
        4. IO something 与Int 不一样,无论我们为something 选择什么。

        GHC 只报告最后一步。

        如果要打印Int,可以使用print构造一个IO动作:

        main = do
          print (doubleMe 2)
        

        这个问题不会出现在 ghci 中,因为在那里,您可以输入任何类型的表达式。如果类型是IO something,ghci 会为你执行 IO 动作。 (例如:readFile "foo.txt")。如果类型不是IO something,ghci 将添加对print 的调用,然后执行该IO 操作(例如:['a' .. 'z'])。

        在源文件中,如果需要或需要,您必须自己添加对print 的调用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多