为什么我的Haskell代码显示“变量未定义:main”?

20
repl.it网站的Haskell交互式Shell中输入以下内容,它能够完美运行。
let squareMe x = x * x
let myFruit = ["banana", "apple", "kiwi", "orange"]

但是当我将其输入源代码文件中并点击“运行”时,会出现错误:

<interactive>:3:1: error:
     Variable not in scope: main
     Perhaps you meant ‘min’ (imported from Prelude)

我已经花了几个小时尝试理解这个错误并找到解决方案,但是现在我离找到解决方案或理解错误的含义还很远。


3
一个程序需要一个入口点:一个被称为 main 的单子函数。 - Willem Van Onsem
1
你写的不是很正确。代码不完整(我缺少main)。此外,请查看let it be。我建议从头开始阅读那本书。 - KarelG
3个回答

27

Haskell的REPL(GHCi)和实际的Haskell程序有很大不同。

这种差异的原因在于两者的目标不同。首先,GHCi是一个测试区域,而不是代码运行区域。然而,Haskell源文件旨在运行特定的进程,该进程名为main。当您运行源文件时,Haskell编译器(通常是GHC)会查找名为mainIO操作,并尝试运行它。在这种情况下,没有main,所以它失败了。

其次,您键入的内容不是有效的Haskell程序,这些是在GHCi中可以使用的声明,但在Haskell源文件中不可以。以下是在源文件中正确的写法:

squareMe x = x * x

myFruit = ["banana", "apple", "kiwi", "orange"]
注意没有使用let声明变量;Haskell源文件不使用它来声明事物。
请注意,在repl.it上,这仍然会提示main缺失,但是您可以在REPL中放心地引用squareMemyFruit。换句话说,错误仍然会出现,但无关紧要,因为您仍然可以使用文件中编写的任何内容。
如果您想要抑制警告,可以添加以下行:
main :: IO ()    -- This says that main is an IO action.
main = return () -- This tells main to do nothing.

程序可以执行的操作并不限于此。以下是几个例子:

  • main = putStrLn "No errors!" 运行后将打印出 No errors!
  • main = print myFruit 运行后将打印出 ["banana", "apple", "kiwi", "orange"]

请注意,本答案主要适用于特定的网站repl.it,但通常Haskell程序都是按照这种结构组织的。


9
如果您编译Haskell源代码,则需要像编译C程序一样,有一个main符号作为入口点。在编译文件中,您必须跳过let。例如:
squareMe x = x * x

main = putStrLn . show $ squareMe 4

1
不需要在print内使用show:它已经在输入类型上是多态的。 - leftaroundabout
2
打印 x = putStrLn (show x),所以你的代码调用了两次 show。 - melpomene
@leftaroundabout,确实。print 可能甚至不应该出现在 Prelude 中,因为一旦你想要从多个值拼接输出,或者添加换行符等等,它就变成了错误的函数。print = putStrLn . showreadLn = readIO =<< getLine,这两个便利函数似乎都不值得增加教学负担。 - dfeuer
@dfeuer 我觉得 print 太方便了,不太同意这个观点。实际上,我更倾向于认为 ShowRead 不应该在 Prelude 中出现。(除了将任何东西转换为/从字符串进行 IO 的目的外,很少有人会感到需要。) - leftaroundabout
谢谢,我编辑了答案并将 print 替换为 putStrLn - mschmidt

1
如果你所编写的更像是一个库或一组实用程序,而不是完整的程序,你可以将其声明为module。然后GHC会将其编译为一个对象,您可以将它链接到其他程序中,并且您还可以在GHCI中加载它。它不需要包含main例程。
如果您将此保存到.hs文件中:
module Example (squareMe) where

squareMe x = x * x -- Exported to other modules.
myFruit = ["banana", "apple", "kiwi", "orange"] -- Not exported.

使用 GHC 编译此代码会生成一个 .hi 文件和一个 .o 文件,在 GHCI 中运行它将得到以下结果:
GHCi, version 8.0.2: http://www.haskell.org/ghc/  :? for help
Ok, modules loaded: Example (sx-modulexmpl.o).
Prelude Example> squareMe 2
4

您还可以从命令行计算引用库的表达式。 ghc -e "squareMe 2" Example.hs 将打印 4


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接