なんか勢いでAdvent Calendarに登録してしまったので、ネタっぽいのを書いてみます。
#参照透過性
Haskellは純粋関数型言語であり、参照透過性を持つことで有名です。関数の値は引数のみで決まるってやつ。変数だと俗にいう再代入禁止ですね。確かめてみましょう。
main=
putStr a
where a = "hoge\n"
a = "hello, world\n"
ちゃんと怒られます。
Conflicting definitions for ‘a’
letでも試してみましょう。
main = do
let a = "hoge\n"
let a = "Hello, World\n"
in putStr a
これもエラーですね。
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
…ウソです。Haskellを少しでも知ってる人ならわかると思いますが、これはちゃんと動きます。
#doってどうなの?
なんで動くかというとdoが付いてるからですね。doの中には複数のアクション(IOモナド)を書けるので、要は2つのlet式が別のアクションになってるってだけです。最初のletにもinを付けるとわかりやすいかも。
(3日14:29追記 間違ってたっぽい。下のhirataraさんのコメント参照)
main = do
let a = "hoge\n" in putStr a
let a = "Hello, World\n" in putStr a
doを使わずに書き換えるとこんな感じでしょうか。あんまり変わらない。
main =
let a = "hoge\n" in putStr a
>> let a = "Hello, World\n" in putStr a
#doのキモさ
なんでdoについて考えてみようと思ったかっていうと、NTTデータのHaskell部隊ではdo構文が禁止されてるって聞いたからです。
そこで、doを使ってできるだけキモいコードを書こうとがんばってみました。
a = do {return "Hello, World\n"}
main = do
a <- a
putStr a
let a = "hoge\n" in putStr a
putStr a
「どこがキモいんだ」と思う向きもあると思いますが、「a <- a」ってなんかやな感じじゃないですか? 真ん中のとこで世界がぶった切れてる感じ。
doを使わないとこんな感じでしょうか。
a = do {return "Hello, World\n"}
main =
a
>>= \a -> putStr a
>> (\a -> putStr a) "hoge\n"
>> putStr a
キモさ半減ですね。「a <- a」の左側のaが仮引数名なのがバレバレ
ちなみに、↓だと挙動が変わってしまうのも、ちょっとイヤ
a = do {return "Hello, World\n"}
main =
a
>>= \a -> putStr a
>> let a = "hoge\n" in putStr a
>> putStr a
#関数合成を使おう
で、NTTデータのdo構文禁止の件ですけど、do構文の禁止自体が目的というよりは、純粋なロジックの記述にアクションを混ぜるなって意味なんじゃないかなーと想像しています。「アクションを使うと逐次処理的なコードが書けちゃうけど、それは関数型プログラミングじゃないよ」という初心者向けの心得って感じ