関数型プログラミングを知る -Scala,Haskell,Elixir- 研修コースに参加してみた
今回参加した研修コースは 関数型プログラミングを知る -Scala,Haskell,Elixir- です。
以前にもPythonのコースでも関数型プログラミングはチラリと触れられてきましたが、今回はメインテーマが関数型です。
このコースでは関数型プログラミングの特徴である高階関数、パターンマッチなどをHaskellを使って書いてみました。が、関数型言語らしい表現を使うと、簡単なサンプルコードを見る限りは理解できるものの、実際どのように活用できるのか、なかなかイメージが難しいものでした。
一方で、関数型言語らしい特徴ではなく、関数そのもので表現されたプログラムをみると、講師の米山さんがSQLのように見えることもあるという補足もあって、とても意味がわかりやすく感じました。
関数型プログラミングとはどのようなものなのか、キッカケを掴みたい方にはオススメの内容でした!
では、コース内容をレポートします!
もくじ
想定している受講者
- JavaやC#などの非関数型プログラミング言語の中級レベルのスキル
受講目標
- 関数型パラダイムの特徴と従来型パラダイムの違い
- 関数型プログラミング言語に触れてみよう -ScalaやHaskell、Elixirの紹介
- 実習:関数型プログラミング -高階関数の活用から再帰、カリー化、モナドまで
講師紹介
プログラミング系コースではお馴染みの 米山 学 さんです。
関数型プログラミングについて
- 明確な定義はない
- 国内ではここ5年ぐらいで注目されている
- ジョン・マッカーシー(AI研究の第一人者) がLispを発表したのが最初
- 純粋か非純粋か
- 引数1つに対して、1つの値を返すのが純粋
- 純粋にHaskellやElmなどがある
- 非純粋にScheme、ErlangやOcamlなどがある
- ハイブリッド型
- JavaScript
- RubyもLispからインスパイアされて開発したとインタビューがある
- ScalaはJavaVMでも動く
- Rubyをベースに開発された言語が Elixir
- 各言語だいたい関数型の特徴を取り入れている
- Python、PHP、C#、Javaなど
- 実はJavaが一番取り入れるのが遅かった
- TwitterやLinkedInなどでScalaが採用されている
- 今日は実際Haskellなどを書いてみて体験
関数型プログラミングの採用例では、Scalaで ScalaMatsuri という大規模なカンファレンスが開催されていますが、そのスポンサーやスポンサーセッションを見ると、どのように採用されているのか分かりますね。
関数型の特徴
- 関数がファースト・クラス・オブジェクトと言っているがオブジェクト指向のオブジェクトではない
- 第一級オブジェクトという
- 代入とは言わず束縛という
- 変数は変更しない
- 関数を他の関数に渡すことができる
- 関数を戻り値として受け取ることもできる
オブジェクト指向との比較
- オブジェクト指向:クラスを 名詞 (ロール) に分解する
- メソッドをデータと一緒に扱う
- 人間的な世界観
- 関数型は 動詞 に分解する
- 1つの小さい関数を扱う
- 数学的な世界観
- 例えばラムダ式も数学者が考案
- SQLを想定するとわかりやすい
SELECT
FROM
WHERE
やはり馴染みのあるオブジェクト指向と対比すると、とても特徴が出ますね。
続いて、Javaで書き方を比較してもらいました。
要件
「従業員リストから、部署 が「営業部」で給与 が「 3000 」よりも大きい「従業員名」を抽出して、新たなリストを作成する」
- Java8以前
- Java8以降
確かにSQLのような書き方で、わかりやすいですね。
なお、繰り返しや分岐は、繰り返し関数や分岐関数を使用するとのことで、なんでも関数なんですね。
関数?
- 関数型言語の関数は純粋関数と呼ばれる
- 通常言語のように処理のまとまりではない
- とはいえ、純粋関数だけを扱うわけではない
- 純粋関数
- 必ず引数を取り、必ず戻り値を返す
- 非正格 (Strict/Non-Strict) な評価がデフォルト
- 正格 (先行評価する)
- f(x) の x に渡される a がわかっている
- 非正格
- あとで a がわかる (遅延評価)
- 正格 (先行評価する)
- 参照透過性が保たれる
- 式そのものだけで評価される
- 同じ引数を与えると、同じ結果が返る
- オブジェクト指向との大きな違い
- あえてオブジェクト指向でやるなら `static`
- 副作用がない
- 関数以外の例えば、状態が変わる、例えば、コンソール出力するというのもNG
- なのできれいなものと汚いものを分ける
- 副作用がある処理を分ける
純粋関数とはかなり強い制約ですね。
またJavaのコードで参照透過性や副作用について補足いただきました。
- 参照透過性が無い
- 副作用が無い
- 参照透過性あり
- 副作用がある
- 参照透過性がない // a と b に異なる変数があるとNG
- 副作用がない
関数型言語のメリット・デメリット
メリット
- プログラムがシンプル
- テストがしやすい
- 先の参照透過性がなく副作用がある場合、単体テストしにくい
- 並列処理
- 関数の処理を複数のCPUに分けて、最後にマージすることができる
デメリット
- 抽象的でわかりにくいこともある
米山さんからは 「数学的な処理を多用するものやデータ構造に対して分析する、などに向いている。ビジネスアプリケーションの開発には合わないかも知れない」と解説され、フムフムなどと理解していたつもりでしたが、ここから実際に関数型言語らしい表現に突入すると、それを体感できました。。。
関数型言語を書いてみよう
- Haskell,Scale,Elixir を選んだのは世界的に人気があって、書き方がいろいろ違うので選んだ
- 実行結果をスグに確かめられるREPLでやる
ということでしたが、時間の都合上、Haskellメインで解説されました。
Haskell
- 実行環境はGHIを使う
- 動的型付け
- 演算子も関数
純粋関数なので1つ以上の引数を扱うことがない -> カリー化で対応する
- ラムダ式
->
=>
が引数と関数を区切る記号としてよく使うλ
ラムダ
高階関数
- 活用
- 数学的な処理
- 統計的な処理
高階関数の応用例として、Map関数やFilter関数、Reduce関数なども紹介いただきました。
関数合成
(f ◦ g)(x) = f(g(x))
- ラムダ式を採用した書き方
- 右側から実行される
再帰
- 関数の中から自関数を呼び出す
- パターンマッチと呼ばれるやり方
- if のような制御構造を現したいとき
- if文を使った例
- パターンマッチ
カリー化関数
- Haskell Curryが発表
- 純粋関数は複数の引数を取れないので、これに対応する
- 複数の引数を取る関数を高階関数に変換する
- 高階関数を連続でやっている
何とかコードを見ながら、理解は出来たものの、正直に申し上げると、私には関数型は早すぎた。。orz 詳細説明できません。スイマセン。。。
とはいえ、中には数学素養がある方であれば、はじめて触るプログラミング言語がHaskellで出来ることもあるそうです。(!!)
ということで、関数型の特徴を説明して、コースは終了しました。
まとめ
このコースでは関数型プログラミングの概念や特徴を、主にオブジェクト指向との比較で解説いただきました。このことでどこが違うのか、とてもすんなり理解できました。
一方で、関数型の特徴であるパターンマッチやカリー化の点はもう少し掘り下げて書ける時間があると、イメージが湧きそうです。またHaskellを使ったので、関数型とHaskellどちらも学習コストが掛かってしまったので、もしかすると、JavaやJavaScriptを題材にした方が関数型の特徴に集中できたかも知れません。
このあたりはコース後に、米山さんとお話していたので、次期では変更されるかも知れません。
なお個人的な素人考えでは、今までのデータオブジェクトではなく、関数という式を関数で繋ぐ、というイメージを持てましたので、関数型プログラミングとはなんぞやと思っている方にはとてもオススメなのではと思っています。
SEカレッジが気になった方はこちらからお気軽にお問合わせください!!
SEカレッジの詳細をご覧になりたい方はこちら!!
SEプラスにしかないコンテンツや、研修サービスの運営情報を発信しています。