2015年2月19日
2015年の最優先事項は関数型プログラミング!
本記事は、原著者の許諾のもとに翻訳・掲載しております。
—もはやOOP(オブジェクト指向プログラミング)は”クラウドモンスター”から私たちを守りきれない
おそらくあなたは、”Clojure”、”Scala”、”Erlang”といった言葉や、”Javaにラムダ式が導入された”という話を聞いたことがあるでしょう。そしてそれらの言葉が”関数型プログラミング”と関連があるのをご存じかもしれません。プログラミングコミュニティに参加していれば、おそらく既にこのテーマが議題に上がっているでしょう。
Googleで”関数型プログラミング”を検索しても、目新しいものは何も見つかりません。言語の中で2番目に古い言語は、関数型プログラミングを利用しています。1950年代に登場した、Lispという言語です。では一体なぜ人々は、今になって関数型プログラミングに沸き立っているのでしょうか? およそ60年も経っているのに?
初期の頃、コンピュータは実に遅かった
信じられないような話ですが、コンピュータはDOMよりもずっと処理速度が遅かったのです。これは本当の話です。そして当時は、プログラミング言語の設計と実装に関する主要な考え方が2つありました。
- ノイマン型から出発し、抽象化を追加する
- 数学から出発し、抽象化を削除する
当時のコンピュータに高い処理能力はなく、関数型プログラムを評価するためのあらゆる抽象化の処理には不向きでした。Lispは処理が極端に遅い状態になり、ジョブに適していませんでした。それから命令型プログラミングが優勢になり始め、とりわけCが優位に立ちました。
しかし、コンピュータは大幅に改善した
現在は、アプリケーションがどの言語で記述されているかをあまり気にすることなく、ほとんどのアプリケーションを実行できると言えます。とうとう、関数型言語がセカンドチャンスを手にしたのです。
関数型プログラミング 50.5
ここでは、関数型プログラミング(FP)の概要を説明するわけではありません。このセクションの最後には、FPがどんなものなのか、そしてこれからあなたがFPを始める方法が分かるはずです。
関数型プログラミングは、関数を持つプログラミングだと考えることができます。実際に、今あなたが想像するよりもずっと、そのままの意味です。ある関数の関数を作成し、関数を合成します(学校で習ったf ∘ gを覚えていますか? これは今後役に立ちます)。それだけです。
以下は、FPの特徴のリストです(全てを網羅しているわけではありません)。
- 第一級関数
- 高階関数
- 純粋関数
- クロージャ
- 不変状態
ここでは、 風変わりな 名前を気にする必要はありません。意味だけ理解してください。
第一級関数 とは、関数を変数に格納できる性質のことです。あなたは既に、JavaScriptで次のような処理を行ったことがあると思います。
ここでは、aとbを受け取ってa + bを返す匿名関数を、addという名前の変数に格納しています。
高階関数 とは、関数を返したり、他の関数を引数として受け取ったりできる関数のことです。
もう一度、JavaScriptの例です。
または
これらは両方とも高階関数の例です。たとえこのようなコードを書いたことがなくても、おそらくどこかでこのパターンを目にしたことはあるでしょう。
純粋関数 とは、値を変更しない関数のことです。データを受け取ってデータを出力するだけのこの関数は、私たちが愛する数学の関数と似ています。もし、関数 f に 2 を渡すと 10 が返されるとしたら、その関数は常に 10 を返します。環境やスレッド、評価の順番は、問題ではありません。プログラムの他の部分に副作用が起きないので、これは実に強力な概念です。
クロージャ とは、戻り値となる特定の関数だけがアクセスできるデータを、関数の内部に保存できる構造のことです。言い換えると、戻り値となる関数はその実行環境を維持します。
高階関数の2つ目の例をもう一度見てください。変数aは閉じ込められていて、戻り値となる関数からしかアクセスできません。実際には、クロージャは厳密にはFPの機能ではなく、最適化です。
不変状態 とは、状態を全く変更できないということです(新しい状態を取得することはできても)。以下のコード(OCamlで記述)では、プログラムの中で x と 5 を置き換えて使うことができます。 x は永遠に 5 のままです。
良い機能というよりはむしろ、ほとんどマイナス面にしか見えません。しかし、それがあなたを救うことになると分かります。
もはやオブジェクト指向プログラミングは私たちを守りきれない
私たちはとうとう、分散処理や並行処理を実行するアプリケーションを持つようになりました。残念なことに、まだその準備はできていません。並行処理や並列処理を行う”現行の”(すなわち最も使用されている)モデルでは、たとえ問題を解決できても、多くの複雑さが伴います。
より優れたアプリケーションには、そのためのシンプルで信頼性の高い方法が必要です。上で説明したFPの機能を覚えていますか? 純粋関数や不変状態は? そうです。既に取得した結果と異なる結果を出すことのない1つの関数を、別々のコアやマシンで1,000回実行できるということです。つまり、1つのコアで実行するコードを1,000個のコアで使用することもできます。これでまた救われます。
「でも、なぜOOPを使い続けてはいけないの?」
少なくとも並行処理や並列処理に関しては、もうOOPはあなたを守りきれません。というのも、OOPが可変状態に直接依存しているからです(OOP実装として最も一般的な、命令型言語において)。呼び出すオブジェクトのメソッドが、現在の self や this を変えるものと想定されているのです。全てのスレッドが正しくアップデートされ同期するよう保っていくには、かなり複雑な対処が必要となるでしょう。
私はここで、現在あなたの使っているパラダイムが何であれFPへ移行すべきだと主張するつもりはありませんが( そうすべきだと言う人もいるでしょう が)、FPをマスターする必要があるのは間違いありません。JavaとC++11は既に、ラムダ式を取り入れているのです。近いうちに、ほぼ全ての現代的でメンテナンスされている言語は、FPの機能に依存するようになるでしょう。既に大半はそうなっています。
ただしお伝えしておくと、可変状態を使うのをやめることにはならないでしょう。有用なプログラムを書くには、やはりIOなどを使う必要があります。FPが与えてくれる主な考え方は、可変状態は必要な時にだけ使うということです。
「クラウド向けに開発していない場合でも、FPは本当に必要?」
はい。
関数型プログラミングは、優れたプログラムを書いたり、解決すべき問題について論理的に考えたりするのに役立つでしょう。
「試してみたら、複雑すぎて読みにくい」
何事も最初は難しいものです。あなたは、プログラミングやOOPにしても、きっと苦労して学んできたはずです。おそらくOOPで何かを始めた時は、初めてプログラムを書いた時よりもずっと取り組みやすかったでしょう。その主な理由は、変数宣言やforループ、whileループといったよく使われる用語に既になじんでいたことです。
FPを始めるということは、プログラミングをまた初めから学ぶのに近いものがあります(言語によっては、本当に最初からやり直す感じになるでしょう)。
FPのコードは読みにくいと考える人も多いかもしれません。命令型言語を学んだ人にとっては、関数型プログラムは暗号のように見えるでしょう。実際に暗号なのではなく、用語をまだ知らないからです。基礎を身につければ、もっと読みやすくなります。
以下のプログラムをご覧ください。HaskellとJavaScript(命令型のスタイルを使用)の両方で書いてあります。
非常にシンプルなプログラムです。ユーザが7を入力したら成功のメッセージを出力し、それ以外ならエラーメッセージを出力します。Haskellのコードがたった2行で済んでいるというのは、暗号のように見えるかもしれません(差し当たって1行目は無視してください。単なる”型注釈”です)。でも実際のところ、いったんパターンマッチ機能を理解すれば、本当にシンプルなのです(パターンマッチはHaskellなどのFP言語で実装されています)。
Haskellが行っていること:
guess関数の受け取る引数が 7 と等しいなら” Much 7 very wow. “(7だ。すごい。)を返し、それ以外なら” Ooops, try again. “(残念。やり直し。)を返す。
そしてJavaScriptのコードもまさに同じ処理をしていますが、Haskellのほうはプログラマが与えた”パターン”とのマッチングを行っています。
代わりにifやelseさえ使うことができれば、その必要はないように思えるかもしれません。ですが、複雑なデータ構造を扱う時には大きな威力を発揮します。
上のプログラムの plus1 は、Intのリストを受け取ってその各要素に1を加える関数です。空リスト[]とのマッチングを行い(要素がなければ別の空リストを返します)、そして空でないリストとのマッチングを、定義したパターンによって行います。その定義では、リストの最初の要素をx、それ以外の要素をxsと名づけています。その後、加算を行い、再帰呼び出しを使って連結しているのです。
この plus1 関数を、命令型スタイルで2行のコードに読みやすいまま書き直そうとすると、大いに悩んでしまうことでしょう。
では、始めよう
関数型プログラミングを扱ったコンテンツはたくさんありますが、以下はぜひ参考にしていただきたいものです。
- 「 Principles of Functional Programming in Scala 」(Scalaによる関数型プログラミングの原理)
- 「 Introduction to Functional Programming 」(関数型プログラミング入門)( コンテンツ )
- 「 Paradigms of Computer Programming — Fundamentals 」(コンピュータプログラミングのパラダイム:基礎)
残念ながら、これらのコースの受講期間は昨年末でしたが、アーカイブのコンテンツにアクセスでき、映像はYouTubeでも利用可能です。
一方、書籍で学びたい場合は、以下が大変お薦めです。
- 『 Structure and Interpretation of Computer Programs 』(邦訳書『計算機プログラムの構造と解釈』)
- 『 How to Design Programs 』(プログラム設計方法)
- 『 Concepts, Techniques, and Models of Computer Programming 』(邦訳書『コンピュータプログラミングの概念・技法・モデル』)
この新しい年があなたにとって、関数型の良い1年になりますように。☺
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
- Twitter: @yosuke_furukawa
- Github: yosuke-furukawa