関数プログラミング実践入門 の要約
関数プログラミングとは、プログラミングパラダイムの1つ。
有名なプログラミングパラダイムとして、以下の3つがある。
- 命令型プログラミングのパラダイム
- オブジェクト指向プログラミングのパラダイム
- 関数プログラミングのパラダイム
プログラムとは、計算機が行うべき命令の列
であるとするパラダイム。
プログラムとは、オブジェクトとそのメッセージング
であるとするパラダイム。
プログラムとは、関数
であるとするパラダイム。値に「関数」を適用していくことで計算をすすめる。
関数プログラミングにおける関数とは、数学的な意味での関数。すなわち、与えられた入力の値のみから出力となる値をただ1つ決める規則
のこと。言い換えると、プログラミングが対象とする処理内容のうち、副作用のないもの。
副作用とは、状態を参照し、あるいは状態に変化を与えることで、次回以降の結果にまで影響を与える効果
のこと。
関数型言語とは、関数が第一級の対象である言語のこと。
「第一級の対象である」とは、その言語において単なる値と同じように扱われるということ。言い換えると、以下の特徴を与えられているということ。
- リテラルがある
- 実行時に生成できる
- 変数に入れて扱える
- 手続きや関数に引数として与えることができる
- 手続きや関数の結果として返すことができる
関数型言語では、文字列や数値と同様に、関数にもリテラルが与えられている。たとえば、引数に1を足す関数は
(λ x . x + 1)
という風に表せる。この関数に2を適用すると
(λ x . x + 1) (2) = 2 + 1 = 3
関数型言語では、(関数合成、部分適用や高階関数等によって)関数を実行時に生成できる。
関数型言語では、関数を変数に入れて扱うことができる。
定義域が特定の関数の集合となるような関数を作ることができる。例えば、「関数を引数にとって、その関数に2を適用した結果になる」関数を以下のように作れる。
(λ f . f(2))
この関数に先程の関数(λ x . x + 1)
を適用すると
(λ f . f(2)) ((λ x . x + 1)) = 2 + 1 = 3
となる。
値域が特定の関数の集合となるような関数を作ることができる。
命令型言語と関数型言語の違いは主に2つ。
- 手続きを書く vs 結果の性質を宣言する
- 代入 vs 束縛
命令型言語では、ある目的を達成するため、
- 値をどこに格納するのか(代入)
- どこから値を取り出してくるのか(参照)
- 次にどの手続に飛ぶのか(手続きの呼び出し)
といったような挙動を記述する。
関数型言語では、結果の性質を宣言する
ことによって目的を達成する。
関数型言語では、変数への代入は許されていないか、もしくは非常に限定されている。代入の代わりに、一度変数の値を決めたら変更不可能になる束縛
を用いる。基本的にすべての変数を定数として扱うということ。
- Clojure
- Coq
- Erlang
- F#
- Haskell
- Idris
- OCaml
- Scala
- SML
以下の6つの視点で分類する^1。
- 動的型付けと静的型付け
- 型付けの強弱
- 純粋
- 型推論
- 依存型
- 評価戦略
関数型言語には、静的型付け言語と動的型付け言語とが存在する。静的型付け言語とは、型検査をコンパイル時に行う。動的型付け言語は、実行時に型検査を行う。
型検査(型に整合性があるかを検査する機能)に成功すれば安全性が保証される型付けを強い型付け
、型検査に成功しても安全性が保証されない型付けを弱い型付け
という。
純粋とは、同じ式はいつ評価しても同じ結果になる参照透過性
という性質を持っていること。
型推論とは、陽に与えられた型の情報から、陽に与えられていない部分の型も推論してくれる機能。
依存型とは、他の型に依存した型や、値に依存した型を作れる機能。
評価戦略とは、どのような順番で式を評価するか
という規則のこと。評価戦略には大きく2種類ある。
- 積極評価: 引数を渡される前に評価する
- 遅延評価: 必要になるまで評価されない
上記の6つの視点で先程あげた関数型言語を分類すると以下のようになる^2。
- これからの関数型言語は、言語の制約の記述力を高め、その制約を言語やライブラリが把握・利用できる方向に進むだろう
- 航空・宇宙・医療・原子力等、システムのバグが人命等に関わる分野で今後採用される可能性がある
等
関数プログラミングのメリットは大きく以下の5つである。
- コード量が少なくなる←かと言って読むのに必要な時間が減るかというとどうなんだろう
- 最適化がしやすい
- 並行/並列化がしやすい
- バグり/バグらせにくい
- ドキュメントが少なくなる
抽象的な表現力が高く、かつ宣言的に記述できるため、記述が簡潔になる^8。
通常よく行われる最適化手法(ループ、データフロー、SSAなどの最適化・アセンブラレベルのコード生成最適化)に加えて、数学的な性質を利用した最適化を行いやすい^9。
- いくつかの関数型言語ではSTMというDBのトランザクションおよびリトライ機構に似た機構を備えており、排他制御を簡単に扱える。
- (純粋な言語は)参照透過性を満たしていることが自明であるため、簡単に並列化できる。
高度な制約条件を型として表現でき、かつその制約条件が守られていることをコンパイル時にチェックできるため、『正しくない』使い方がされづらい。
高度な制約条件が守られているかどうか言語がチェックしてくれるため、その制約条件についてのドキュメンテーションの必要がなくなる。
関数型言語のメリットは大きく以下の5つである。
- 宣言的であることのメリット
- 制約の充足をチェックしてくれるメリット
- 型と型検査があることのメリット
- 型推論のメリット
- 束縛によるメリット
「結果が満たすべき性質」を書くだけで良いため、記述が簡潔になる。
言語機能として課せられたか、もしくはプログラマが与えた制約が守られていることを言語がチェックしてくれる。それによって、プログラマが注意を払う必要のある事柄が減る。
関数の引数として、その関数の定義域に収まらない値が与えられる(合成できない関数同士を合成してしまう場合等)ことを事前に防ぐことができる。
逐一型を書かなくて良いため楽。
破壊的代入がないため、コールドリーディングが楽^10。
- 厳密にいうと型付きの言語と型なしの言語という分類もあるが、現在実用的な段階にある言語はほぼ型付きの言語なので、型システムを持つことを前提に分類する。
- https://ja.m.wikipedia.org/wiki/%E9%96%A2%E6%95%B0%E5%9E%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0
- http://twitter.com/
- https://www.linkedin.com/home/?originalSubdomain=jp
- https://foursquare.com/
- https://tabbles.net/
- https://manaba.jp/
- かと言って読むのに必要な時間が減るかというと微妙な気がするけど。
- 命令型言語の方が現在のコンピュータアーキテクチャと相性が良いため、数学的な性質を利用した最適化を行なったとしても、関数型言語のパフォーマンスが命令型言語を上回るかは微妙。
- 純粋であることの必要条件でもある(多分)。