OOPについて学び直しメモ - 🍥shuma_yoshioka に引き続き、雰囲気でプログラミングしているのでFP (Functional Programming, 関数型プログラミング) について雑に学び直した。
学び直しとは言うけど、そもそも関数型プログラミングは勉強したことないし、もっと言うとちゃんとHaskell書いたのも初めてになる。
明確な定義は無いらしいが、言語自体に 関数の再帰呼出しによるプログラムの数式化 を助ける機構があるか否か、がポイントとなるもよう。
Javaはオブジェクトを手続型のように扱えるのがコンセプトとなる言語なので、このような言語は オブジェクト指向型言語 などのように分類されやすい (手続き型言語として分類されにくい) 。よって、手続き型言語には例えば PHP, Python などが挙げられやすい。
対し関数型として挙げられやすい言語は、Lisp, Haskell, OCaml, Erlang, Scala など。(Scalaはハイブリッドと言われがちだが)
Javaが「全てがオブジェクトである」のと同じように、関数型言語は「全てが数式である」と言える。
オブジェクト指向言語は、たとえ同じメソッドであってもそのオブジェクトが持つ状態により暗黙的に値が変わる場合が多々ある。
あまり良い例とは言い難いが、パッと出せるとこで言うと例えば、
class MyCls { private final int state1; private final int state2; public MyCls(int state1, int state2) { this.state1 = state1; this.state2 = state2; } public int calcState(int x) { return x * (state1 + state2); } } // App.java public class App { public static void main(String[] args) { MyCls obj = new MyCls(2, 3); System.out.println(obj.calcState(5)) // 上の行を知らないと、ここの式で何が出力されるかわからない } }
というのが発生する。これは決して常に悪く作用するわけではないが、参照透過性が無いと言える。
参照透過性は、入力が同じなら同じ作用と同じ値を返す状態で成立する。つまり手計算した値を直接埋め込んでも大丈夫な状態でなければいけない。
対し関数型言語とし例えばHaskellの場合、以下のようにするだろう。
data MyState = MyState { state1 :: Int, state2 :: Int } calcState x y = x * (state1 y + state2 y) main = do let my = MyState { state1 = 2, state2 = 3 } print $ calcState 5 my
このような参照透過性を実現する強い制約が関数型言語をそれたらしめていると言える。
そしてこのような制約のおかげで、再帰呼出しによる数式化が助けられている。
fibSeq x y = [x] ++ fibSeq y (x + y) -- または `fibSeq x y = x : fibSeq y (x + y)` main = do print $ take 10 $ fibSeq 0 1 {- フィボナッチ数列を求める簡単な例。 読み方: 1. 第一引数のみを持った配列をつくる 2. xとyを足し合わせたものを求める 3. 再帰呼び出しで配列を取る。第一引数はy、第二引数はさきほど足し合わせたもの 4. それらの配列を連結する。第一引数の次に第二引数という風に結合され、さらにそれを足し合わせたもの、という形に無限に求め続ける 5. `take 10`を使い、先頭から10番目の要素までを取り出す (現れた時点で辞める) 6. それを標準出力へ -}
なおパターンマッチなども特徴的だが、これらはあくまでも言語機能、糖衣構文のようなものでしかないので深くは言及しない。
関数型言語の特徴として、前述のとおり数式化可能なこと、参照透明性が高いことが挙げられる。
そうした特徴は様々な非関数型言語へも取り入れられている。たとえば、JavaのStream, Reactive Extensionsなども関数型言語の影響を受けている。
関数を引数とした関数を高階関数 (ハイオーダー ファンクション) という。Reactive ExtensionsなどのRxと呼ばれるライブラリは、FRP (Functional Reactive Programming) を実現するもの。Reactive、Observableは非同期プログラミングにおける概念なので、ここでは深く言及しない。
またKotlinなどはCollectionsとして同様の高階関数を提供している。
他にも触っておくべきことが多そうだが、恥ずかしながら関数型言語の利用経験が少ないためすぐに思いつかなかった。そのため一旦ここで区切ることにする。
参考にした記事は、
など。