BackGround
筆者は普段、Go、Perl、C言語などを主に使用しプログラムを書いています。
関数型言語は、過去にLispを試食してみた程度です。
最近、すごいH本なるHaskell本を読んでいて、
少しずつ関数型言語なるものを自分の使用する言語へ加えていきたいと考えています。
関数型を勉強するにあたって、同じくHaskellを学んでみたいと考えている方は多いのではないでしょうか。
というわけで、典型的なマルチパラダイム言語を書いている人から見た、Haskellプログラムの雰囲気を、
今回はお伝えしてみようと思います。
Target
- Haskellのソースコードの雰囲気がつかめる
- 構文についての言語仕様にどんなものがあるか知ることができる
What I want to make
よくあるfizzbuzz
プログラムをサンプルコードとして作成したいと思います。
今回、fizzbuzzプログラムの要件(ルール)は、以下のように定義します。
- 要件
- 1~100の数字列を文字列として表現し、標準出力へ出力する
- 数字列の中で、3の倍数は"Fizz"という文字列へ置き換えて出力する
- 数字列の中で、5の倍数は"FizzBuzz"という文字列へ置き換えて出力する
- 数字列の中で、3の倍数かつ5の倍数は"FizzBuzz"という文字列へ置き換えて出力する
SourceCode(Go)
fizzbuzz
は、よくあるマルチパラダイム言語では、例えば以下のようになります。(Goにて作成)
package main
import (
"fmt"
"strconv"
)
func fizzbuzz(num int) string {
if isFizz(num) && isBuzz(num) {
return "Fizzbuzz"
} else if isFizz(num) {
return "Fizz"
} else if isBuzz(num) {
return "Buzz"
} else {
return strconv.Itoa(num)
}
}
func isFizz(num int) bool {
return num%3 == 0
}
func isBuzz(num int) bool {
return num%5 == 0
}
func main() {
for i := 1; i <= 100; i++ {
fmt.Println(fizzbuzz(i))
}
}
では、次は同様の出力が得られるプログラムを、Haskellにて作成してみましょう。
SourceCode
Haskellで作成したfizzbuzzプログラムのソースコードは以下のようになりました。
上のGoによるプログラムと対比させやすい形にしています。
(もしくは、Goのプログラム側をそうさせている)
fizzBuzz :: Int -> String
fizzBuzz n | isFizz && isBuzz = "FizzBuzz"
| isFizz = "Fizz"
| isBuzz = "Buzz"
| otherwise = show n
where
isFizz = n `mod` 3 == 0
isBuzz = n `mod` 5 == 0
main = do
mapM_ putStrLn $ map fizzBuzz [1..100]
このシンプルなプログラムは、「何をするか」ついて、
Haskellを知らない人へも説明がいらないような明快さを持っていると思います。
- Goのプログラムと比較して以下のような違いがある
- コード行数が少ない
-
izFizz
、isBuzz
の定義が、関数の中に内包されており、可読性が高い
Haskellの言語仕様に関わる要素を以下で解説します。
(以下は、Haskellの言語仕様の中でもほんの一部ですが、言語仕様の雰囲気を感じ取ってもらえると思います。)
-
fizzBuzz :: Int -> String
- 関数の型定義を行う
- Int(整数)を取り、String(文字列)を返す関数であるという定義を行っている
- ちなみに2引数である場合は
Int -> Int -> String
となる
-
fizzBuzz n | isFizz && isBuzz = "FizzBuzz"
から始まる一連の定義-
guard
という仕組みを用いて、引数ごとに返す値を定義している -
|
で繋いだ一連の式によって、上から(左から)順番に評価される
-
-
where
- where文という構文
- ブロックスコープで、変数へ値を「束縛」する
- 変数への値の「代入」は純粋関数として「副作用」にあたるため、禁止されている
- ゆえに、Haskellでは、値を束縛した変数への再代入は禁止されている
-
main = do
- ざっくりとは、main関数のような位置づけ
- 実体は、
IO monad
という副作用伴うI/O処理を行うmonad
という仕組み
-
mapM_ putStrLn $ map fizzBuzz [1..100]
-
map
は、Perlなどでもあるmap
と同様のもの -
mapM
は説明が難しい- とりあえず「
IO monad
という仕組みの中で、リストを出力するmap処理を行うために必要」と認識しておく - 正確な理解はより詳細な
monad
の理解が必要
- とりあえず「
-
fizzbuzzプログラムを書くだけでも、マルチパラダイム言語にはないような言語仕様に従うことになります。
小さなプログラムでも、エラーを出しながら意図するものができるまで書いてみることが理解へ近道と思いました。
Roundup
- 言語のパラダイムが大きく異なると学習コストは高くなる
- 関数型ならではの言語仕様が何を目的としているかを意識すると、言語仕様への理解がし易い
- とはいえ、Haskellはマルチパラダイム言語と共通するとことも沢山あるので、お手上げとはならず学んでいける感触である