当我在 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)
我花了几个小时试图理解这个错误并提出解决方案,但距离找到解决方案或理解错误的含义还差得很远。
Haskell REPL (GHCi) 和实际的 Haskell 程序有很大不同。
造成这种差异的原因是两种格式的目标。首先,GHCi是一个测试区,而不是一个代码运行区。然而,Haskell 源文件旨在运行某个名为
main
的进程。当您运行源文件时,Haskell 编译器(通常是 GHC)会查找名为 IO
的 main
操作,并尝试运行它。在这种情况下,没有main
,所以失败了。
其次,您输入的不是有效的 Haskell 程序,这些声明在 GHCi 中没问题,但在 Haskell 源代码中则不然。这在源文件中是正确的:
squareMe x = x * x
myFruit = ["banana", "apple", "kiwi", "orange"]
注意缺少
let
; Haskell 源文件不使用它来声明事物。
请注意,在 repl.it 上,这仍然会抱怨缺少
main
,但您可以放心地在 REPL 中引用 squareMe
和 myFruit
。换句话说,错误仍然会出现,但这并不重要,因为您仍然可以使用您在文件中编写的任何内容。
如果您想抑制警告,您可以编写以下行:
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 程序的结构。
如果您编译 Haskell 源代码,则需要有一个
main
符号作为入口点,就像编译时一样。一个 C 程序。此外,在编译文件中,您必须跳过 let
。例如
squareMe x = x * x
main = putStrLn . show $ squareMe 4
如果您正在编写的内容更像是一个库或一组实用例程,而不是一个完整的程序,您可以将其声明为
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
。
在文本编辑器中打开文件并将其另存为 somecode.hs。
在 somecode.hs:
main = do
let squareMe x = x * x
let myFruit = ["banana", "apple", "kiwi", "orange"]
保存并在终端中运行:
runghc somecode.hs