Haskellのコードはこんな感じ
さて、理屈をこねるよりもとりあえずコードを書いてみましょう。 サンプルコードを用意しました。
Haskell
square x = x * x -- xの平方を返却する関数
main = do
x <- readLn -- xの値を入力
putStr $ square x -- xの平方を計算して出力
C
int square (int x) { return x * x; }
int main(void)
{
int x;
scanf("%d", &x);
printf("%d", square(x));
}
いちおう「入力」「計算」「出力」の3つをおこなうプログラムです。
HaskellとC言語のコードを用意しましたが、いずれも同じ処理を行います。
こうやって見ると、
Haskellのコードが難解だという印象はあまり受けないのではないでしょうか。
整数の平方を計算する関数を別に定義しておいて、
それをmain関数から呼び出すという構造はC言語のそれと変わりませんね。
関数の書き方
では、square関数から順番に見ていきましょう。
Haskellでは、関数定義を次のように行います。
関数名 引数1 引数2 ... 引数n = 返却値
square x = x * x
add x y = x + y
構造だけ見れば非常にシンプルで、難解な部分はありませんね。
引数の型が定義されていないことに注目しましょう。
Haskellには「型推論(type inference)」という機能があり、
コンパイル時に型を推定してくれるので、型の明示が必須ではありません。
明示したい場合は次のように書きます。
関数名 :: 引数1の型 -> 引数2の型 -> ... -> 引数nの型 -> 返却値の型
square :: Int -> Int
add :: Double -> Double -> Double
基本的にはコード全体で矛盾なく型が推定されれば 型の明示がなくともコンパイルは通るのですが、 読みやすいコードを書くという観点から 引数の多い関数などについては明示しておくのが親切でしょう。
do文について
続いてmain関数について見ていきましょう。 Haskellのエントリポイントの名前はC言語と同様'main'です。
main関数の「=」の後に、square関数にはない'do'がありますね。 これは後に続く文が順次構造をもったコードであることを宣言しています。
逆に言えば、これを書かないとそれぞれの関数が
上から順番に実行されるとは限らないということになります。
なぜそんな面倒なことになっているのかというと、
それはHaskellのもつ最大の強みの一つである「遅延評価」を実現するためなのですが、
それについてはまた今度お話しすることにします。
とりあえず、main関数ではdo文を書いて上から順に実行してもらうのだ、 と覚えておいてください!
「読みやすさ」だけじゃない!
引き続きmain関数を見ていきましょう。
'do'以降の部分を見てみると、インデントによって字下げされていますね。
これは単に読みやすくするためのものではありません。
'do'がどの式をまとめているのか、というのをコンパイラに伝えるためのものなのです。
ですから、何も考えずにインデントをいじってしまうと、
コンパイルを通らなかったり予想外の動作が起きたりするので要注意です。
main = do x <- readLn -- これはOK!
putStr $ square x
main = do -- これもOK!
x <- readLn
putStr $ square x
main = do -- これはダメ!
x <- readLn
putStr $ square x
このように、'do'によってまとめられている式の先頭は必ずそろえるようにしましょう。 1つ目と2つ目のパターンはどちらでもOKですが、3つ目は先頭がそろっていないのでNGです。
さて今回はこのあたりにしておきましょう。 次回も引き続きサンプルコードを紐解いていきます。 次は入出力について見ていきましょう。それではお楽しみに!