HaskellでHello World!

こんばんは、south37です!

最近はHaskellネタばっか書いてる訳ですが、ここでいよいよ「Hello World」をしてみたいと思います!待ちに待ったHello Worldですよ!楽しみですね!!

...と喜ぶのは構わないですが、ここで一つ疑問が出てきます。他の言語なら真っ先にやるような「Hello World」を、なぜ今更やるのでしょうか?「Hello World」はそんなに難しい事なのでしょうか?他の言語では、とてもそうは思えませんでしたが?

...今回は、そのへんの疑問にもお答え出来たらいいなと思います!

何はともあれHello World

さて、案ずるより産むが易しという言葉もありますし、とりあえずやってみましょう!まずは、次のコードを適当なファイル名で保存します。今回は、helloWorld.hsとでも名付けましょう。

main = putStrLn "Hello World!"

次に、シェルからghcコマンドを使ってhelloWorldプログラムをコンパイルします。

ghc --make helloWorld

そうすると、helloWorldという名前の実行ファイルが出来ているはずです。最後は、これをシェルから実行します。

./helloWorld
> Hello World!

ハイ、これだけです!一瞬で終わってしまいました。思った以上に簡単でしたね!!

では、何故こんな簡単な事を、これまでやってこなかったんでしょうか?

その疑問に答える第一歩として、まずは、helloWrold.hsに出てきたパーツ一つ一つについて考えてみましょう。mainはただの変数名、"Hello World!"もただString型の値を生成するリテラルであると思うと、注目すべきはputStrLnだと分かります。では、putStrLnの正体は何なんでしょうか?

入出力の「型」と「関数」

実は、putStrLnはただの関数です。helloWorld.hsの中では、putStrLn"Hello Wrold!"というString型の値を受け取り、返した値にmainという名前がつけられているだけです。では、putStrLnの返す値とは何なんでしょうか?

実は、putStrLnが返すのはIO ()型(()はunitと呼ぶ)を持つ値であり、そのようなIO a型の値は「I/Oアクション」と呼ばれます。I/Oというのは入力(Input)、出力(Output)のI/Oで、要するに外界との入出力を行うのが「I/Oアクション」です。

Haskellでは、「I/Oアクション」であるような値を特別扱いしていて、mainという名前がつけられるとその「I/Oアクション」が実行されます。例えば、putStrLnは引数としてStringを受け取り、受け取ったStringを標準出力へ出力するという「I/Oアクション」を返します。

つまり、上記の例では

putStrLn :: String -> IO ()

のような型を持つputStrLn"Hello World!" :: Stringを受け取って、「Hello World!を標準出力へ出力する」という「I/Oアクション」を返し、それにmainという名前がつけられた為に実行時にHello World!の出力が行われた訳です。

要は、入出力もただの値という事です。何だか正体が見えてきましたね!

今回は「I/Oアクション」を返すものとしてputStrLnだけを見ましたが、この他にも外部からの入力を行う為のgetLine関数や、複数の「I/Oアクション」を糊付けして一つの「I/Oアクション」とする為の「do記法」などがあります。

結局、何が難しかったの?

ざっくりまとめると、HaskellにおいてIOを行うのは、コードとしては簡単です。ただ、その型についてちゃんと理解するのは、意外と大変だったりします。今回は最もシンプルな例でしたので理解しやすかったですが、次回以降、Monadとかその辺の話もしていこうと思います。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!