基本的“你好,世界!” Haskell中的程序可以用一两行简洁地表示:
main :: IO () main = putStrLn "Hello, World!"
第一行是一个可选的类型注释,指示它main是type的值IO (),表示一个I / O操作,该操作“计算” type的值()(读取为“ unit”;空元组不传达任何信息),除了对外部世界(这里,在终端上打印字符串)。通常会省略此类型注释,main因为它是唯一可能的类型。
将其放入helloworld.hs文件中,然后使用Haskell编译器(例如GHC)进行编译:
ghc helloworld.hs
执行编译的文件将导致输出"Hello, World!"打印到屏幕上:
./helloworld Hello, World!
可替代地,runhaskell或runghc使得能够无需编译它运行在解释模式下程序:
runhaskell helloworld.hs
还可以使用交互式REPL代替编译。它随大多数Haskell环境一起提供,例如ghciGHC编译器附带的环境:
ghci> putStrLn "你好,世界!" Hello, World! ghci>
或者,使用load(或:l)从文件将脚本加载到ghci中:
ghci> :load helloworld
:reload(或:r)重新加载ghci中的所有内容:
Prelude> :lhelloworld.hs [1 of 1] Compiling Main ( helloworld.hs, interpreted ) <some time later after some edits> *Main> :r Ok, modules loaded: Main.
第一行是类型签名,声明了以下类型main:
main :: IO ()
类型的值IO ()描述了可以与外界交互的动作。
因为Haskell有一个完全成熟的辛德米尔纳型系统,允许自动类型推断,类型签名在技术上是可选的:如果你简单地忽略main :: IO (),编译器将能够通过分析来推断其自己的类型定义的main。但是,不为顶级定义编写类型签名是非常糟糕的样式。原因包括:
Haskell中的类型签名是非常有用的文档,因为类型系统表现力强,以至于您经常可以通过查看其类型来了解函数对哪种类型有好处。可以使用GHCi之类的工具方便地访问此“文档”。与普通文档不同,编译器的类型检查器将确保它实际上与函数定义匹配!
类型签名将错误保留在本地。如果在定义中犯了一个错误而没有提供其类型签名,则编译器可能不会立即报告错误,而是简单地为它推断出一个毫无意义的类型,并使用它进行类型检查。使用该值时,您可能会收到一条隐秘的错误消息。有了签名,编译器非常善于发现错误发生的位置。
第二行完成实际工作:
main = putStrLn "Hello, World!"
如果您使用命令式语言,则可能需要注意此定义也可以写成:
main = do { putStrLn "Hello, World!" ; return () }
或等效地(Haskell具有基于布局的解析;但是请注意,将制表符和空格不一致地混合会混淆此机制):
main = do putStrLn "Hello, World!" return ()
do块中的每一行代表一些monadic(此处为I / O)计算,因此整个do块代表以特定于monad的特定方式组合起来的由这些子步骤组成的整体动作(对于I / O,这意味着只是一个接一个地执行它们)。
该do语法是本身单子一个语法糖,如IO这里和return是制备它的参数,而不执行任何副作用或可能是一个特定的单子定义的一部分附加计算无操作动作。
上面的定义与定义相同main = putStrLn "Hello, World!",因为值putStrLn "Hello, World!"已经具有类型IO ()。视为“陈述”,putStrLn "Hello, World!"可以看作是一个完整的程序,您只需定义main即可引用该程序。
您可以查找putStrLn在线签名:
putStrLn :: String -> IO () -- thus, putStrLn (v :: String) :: IO ()
putStrLn是一个将字符串作为参数并输出I / O操作(即,表示运行时可以执行的程序的值)的函数。运行时始终执行名为的动作main,因此我们只需要将其定义为putStrLn "Hello, World!"。