「関数型プログラミング」の版間の差分

削除された内容 追加された内容
(同じ利用者による、間の7版が非表示)
20行目:
 
'''[[型推論]]'''
: 型推論は[[静的型付け]]の機能であり、コンパイル時の解析でソースコード内のあらゆる関数適用や変数束縛が求める等価性と値のそれが一致しているかをチェックする。値の等価性は推論的型付け視点の「型」になる。同じ型でなかったら計算不可の型エラーになってコンパイルエラーになる。型の推論はプリミティブ記述、データ構築子定義、変数束縛(''Var'')関数定義(''Abs'')関数適用(''App'')等式(''Let'')型構築子定義(''Gen'')型構築子宣言(''Inst'')といったソースコード内のあらゆる記述ポイントを拾い上げて総合解析するという専用の型推論アルゴリズムで行われる。端的に言うとプリミティブという原子の組み合わせとその写像による変遷を精密に辿ってそれぞれの型を判別していると考えてよい。推論的型付けでは変数/引数/返り値に対する型宣言と型注釈は不要になり、むしろ計算全体の整合性を損ねるものとして倦厭される。[[命令型プログラミング|命令型言語]](手続き型やオブジェクト指向)の型推論アルゴリズムは簡素化されているので、型推論の使用対象はローカル変数や引数渡しが中心になる。この型推論の利点は、ローカル変数に型テンプレート的な表現の幅を持たせてソースコードの保守修正作業を容易にすることである。従来の型宣言と型注釈を用いる{{仮リンク|明示的型付け|en|Manifest typing|label=}}と、[[型推論]]の共存はC言語世代プログラミングに対する一つのパラダイムシフトでもある。
: [[命令型プログラミング|命令型プログラミング言語]]での型推論は、ローカル変数の型宣言/型注釈を省略することで表現に幅を持たせるための[[糖衣構文]]に近い機能と考えてよい。[[テンプレート (プログラミング)|型テンプレート]]的な表現の幅である。[[プリミティブ型|リテラル]](数値/実数/文字列)および文脈上で型が明確な[[インスタンス]]の初期代入は、同時に対象変数の型注釈になると再考されたことに基づいている。従来の{{仮リンク|明示的型付け|en|Manifest typing|label=}}(型宣言/型注釈の使用)と[[型推論]]の共存は、C言語世代プログラミングに対する一つのパラダイムシフトでもある。
 
== 特徴 ==
{{出典の明記| date = 2020年5月6日 (水) 02:29 (UTC)| section = 1}}
ここでは関数型プログラミング本来の考え方([[プログラミングパラダイム|パラダイム]])に基づいて説明する。[[文 (プログラミング)|ステートメント]]を基本文にする[[命令型プログラミング|命令型言語]]と、[[式 (プログラミング)|式]]を基本文にする関数型言語はどちらも最終的には[[命令型プログラミング|命令型パラダイム]]に沿った[[機械コード]]に落とし込まれる事になるが、双方の間にはプログラミングに対する考え方に比較的大きな隔たりがあると言える。
 
=== 式と関数 ===
関数型プログラムの基本文は[[式 (プログラミング)|式]](''expression'')である。式は個体(''individual'')である値(''value'')と写像(''mapping'')である関数(''function'')の二つから構成される。関数の定義には[[演算子]](''operator'')も含まれている。値は[[基本データ型]](プリミティブ)と{{仮リンク|複合データ型(コンポジット)|en|Composite data type|label=}}および[[ラムダ計算]]で言われる変数(''variable'')を意味する。変数は[[束縛変数]]と[[自由変数と束縛変数|自由変数]]を指す。評価(''evaluation'')される前の式は、ラムダ計算で言われるネーム(''name'')と同義になる。ネームは数学上の数式または代数式に相当するものである。式内の変数部分が確定される前の式はラムダ抽象(''abstraction'')と同義になる。式内の変数部分を確定するのはラムダ適用(''application'')と同義になる。この式=ネームが評価されると値になり、これはラムダ計算で言われる簡約(''reduction'')と同義になる。式は値と同一視されるのですなわち式と値は相互再帰の関係にある。式内の値は他の式の評価値である事があり、その式内にもまた他の値があるといった具合である。この解釈は[[高階論理|高階述語論理]]''と呼ばれる。高階述語論理=[[高階関数]]の解釈下で引数または返り値として扱われる関数は[[第一級関数]]と呼ばれる。''
 
関数型プログラミングの関数は’’関数の型’’(”(''function type'')で分類される[[存在量化子|存在量]]の値である。プログラム的には式に引数を結び付ける機能であり、これは関数を引数に[[写像|適用]]する(''applying a function to an argument'')とされる。関数の式内の仮引数(''parameter'')箇所に渡された実引数(''argument'')が[[パターンマッチング]]手法で当てはめられ、先行(その時)または遅延(その後)タイミングで評価されて結果値が導出される。この仮引数箇所は束縛変数と呼ばれる。関数呼び出し時とは異なるタイミングで内容が決定される変数箇所は自由変数と呼ばれる。letとwhereで特定の式に向けて定義される変数はその式への束縛変数になる。なお関数型パラダイムでの自由変数の意味合いは他の[[宣言型プログラミング|宣言型パラダイム]]とはやや異なっている。関数適用時に用いられる[[パターンマッチング]]手法は、仮引数パターンの[[選言]]的な列挙を可能にしている。このパターンマッチングは’’関数の型’’に沿った等値性(''equality'')で仮引数と実引数を照合する。更にそれに[[ガード (プログラミング)|ガード]]と呼ばれる値の比較照合と範囲照合を加えることもできる。仮引数が非交和型の場合はその中で列挙されている型との等価性(''equivalent'')でも仮引数と実引数を照合できる。渡される実引数によっては[[ボトム型]]になる関数もありこれは部分関数(''partial function'')と呼ばれる。ボトム型は式ないし関数の評価の失敗した終着点を意味する。演算子はデフォルトの式内容を持ち、その引数が単項演算子なら1個、二項演算子なら2個に限定された関数と同義である。
 
’’関数の型’’は「第1引数の型→第x引数の型→評価値の型」というように形式化されておりこれはカリー化(''currying'')と呼ばれる。例として関数funcの型を<code>func::A→B→C</code>とするとこの場合、A型値に適用されたfuncは<code>B→C</code>という’’関数の型の値’’を返す事になり、それをB型値に適用するとC型の評価値が返る事になる。左からの引数にひとつひとつ適用する形にして、<code>B→C</code>のような中間的な’’関数の型の値’’が導出されるようにする仕組みが関数の[[カリー化]]である。カリー化は写像の[[量化]](''quantify'')を扱う[[二階述語論理]]の実装表現手法ある。カリー化によって関数funcの型は<code>func::A→(B→C)</code>と読み替えられるようになり、この場合にAにのみ適用して<code>B→C</code>という’’関数の型の値’’のまま保留することは部分適用(''partial application'')と呼ばれる。またカリー化による重要概念に関数合成(''function composition'')がある。これは二項の合成演算子<code>.</code>(専用の二項演算子)を関数<code>f::B→C</code>に適用すると<code>(*→B)→(*→C)</code>が導出され、それを関数<code>g::A→B</code>に適用すると<code>(A→B)→(A→C)</code>となり関数<code>f . g::A→C</code>が導出されるというものである。合成演算子の左側の[[定義域]]と右側の[[値域]]が同じ型の場合のみ合成できる。高階関数的な連結である<code>f (g A)</code>と働きかた的には同じであるが、[[パイプライン処理]]の方に該当する関数連結(関数チェーン)と、カリー化に則った関数合成は異なる概念である。カリー化による部分適用や合成演算子から導出された’’関数の型の値’’任意の変数に束縛して扱うのはポイントフリースタイル(''point-free style'')と呼ばれる。ポイントフリースタイルの変数を値に適用すると、他の評価された値が返されるかまた他の’’関数の型の値’’が返される事になる。ポイントフリースタイル変数は<code>var value</code>の書式で自身の右の値を暗黙的に引数として取る。ポイントフリースタイルは[[高階述語論理]]と[[存在量化子]]の表現手法でもあり、[[継続渡しスタイル]]にも応用される。引数を部分適用された演算子はセクションと呼ばれてポイントフリースタイルでよく用いられる。[[カリー化]]準拠の’’関数の型’’は[[型理論]]の指数型(''quotient type'')に分類されるものである。
 
関数は名前付きと名前無しの二通りある。名前無しの関数は専らラムダ抽象を模した構文で定義される。式内に自由変数を内包しない方は単に[[無名関数]]と呼ばれ、自由変数を内包する方はそれを囲い込むという意味で[[クロージャ]]と呼ばれる。自由変数は外部データへの接点になる。[[無名関数]]は引数をピュア[[写像|マッピング]]する純粋関数である。[[クロージャ]]の引数の[[写像|マッピング]]は式内の自由変数に影響され、またその自由変数に作用する事もあるという副作用要素を閉包した非純粋関数である。リスト処理時にリストの各要素への作用子として渡される無名関数またはクロージャは[[反復子|イテレータ]]と呼ばれる。同様にリスト処理時に渡されて各要素を参照しながらそれらの総和値または選別リストまたは更新リストを生成する方は[[ジェネレータ (プログラミング)|ジェネレータ]]と呼ばれる。これは[[イミュータブル]]重視時に多用される。関数の名前は、それに結び付けられた式または式ツリーの[[不動点]]の表現になる。自式の不動点を式内に置いて新たな引数と共に[[高階論理|高階述語論理]]の式として評価する手法は[[再帰]]と呼ばれる。関数の終端式での再帰は実引数の更新+先端式へのアドレスジャンプと同等に見なせるのでもっぱらそちらに最適化されてこれは[[末尾再帰]]と呼ばれる。末尾再帰は論理性を損なわずにスタックフリーの無制限ループを可能にする実装概念として重視されている。
 
=== 値とデータストラクチャ構造 ===
関数型プログラミングの値(''value'')は型(''type'')で分類される[[定数 (プログラミング)|定数]]または[[全称量化子|全称量]]の[[変数 (プログラミング)|変数]]である。これは[[基本データ型]](プリミティブ)と{{仮リンク|複合データ型(コンポジット)|en|Composite data type|label=}}のいずれかで表現される。プリミティブは数値、論理値、文字値、文字列を指す。様々なコンポシットはプリミティブを様々な形式で任意に組み合わせたものがコンポジット複合体であり、そのとしてC言語の[[構造体]]や[[共用体]]などであるを指す。その組み合わせ方に焦点を当てた用語が[[データ構造|データストラクチャ]](''data structure'')である。データストラクチャ構造という概念には[[再帰]]構造、[[アノテーション]]付き構造、[[ガード (プログラミング)|ガード]]付き構造、[[操作的意味論|操作的意味]]付き構造といった様々な暗黙情報を含められるので、コンポジットの具体的形式といった意味で用いられる。関数型言語で用いられるデータストラクチャ構造の代表は[[代数的データ型]]と[[S式]]である。双方ともデータ構築子(''data constructor'')から構築される。まず、プリミティブがデータ構築子によってまとめられる。正確ではないがデータ構築子はC言語の構造体または/共用体と同性質のものと見てよい。データ構築子は入れ子にあり、むしろ言い換えとC言語は直積型で、データ構築子まとめた構造体にし、非交和型のデータ構築子も造を共用体にしてペア定義できしてい他、同名データ構築子は入れ子構造と、自身を入れ子にした再帰構造も定義できる。プリミティブとデータ構築子を任意に組み合わせて代数的データ型やS式といったデータストラクチャ構造が構築される。データストラクチャ構造内のプリミティブとデータ構築子の組み合わせ方はパターン(''pattern'')と呼ばれる。そのパターンが型になり、パターンの構築が型付けになり、パターンを[[量化]](''quantify'')すると型付け値になり、これはターム(''term'')と呼ばれる。タームは冒頭の値(''value'')を指す。データ構築子のパターンの末端は必ずプリミティブになるので、パターン内の全てのプリミティブの値を決定することが量化になる。お互いのパターンがマッチするターム同士は等価(''equivalent'')とされる。この等価は同じ型と読み替えてもよい。等価性はあらゆる計算の可否(計算可能性)を決定する。計算とは関数適用または演算子適用を指し、それらが求める仮引数と実引数にするタームが等価であればその計算は成立する事になる。データストラクチャ構造のパターンは基礎パターンに分解されて解釈される。基礎パターンは[[型理論]]に従って直積型、非交和型、ユニオン型、オプション型、帰納型、ユニット型などに分類されている。
 
[[S式]]は[[二分木|二分木構造]]のデータストラクチャ構造である。これはコンス(''cons'')と呼ばれる二項のデータ構築子の連結で形成される。コンスは二つの要素を持つ[[タプル]]であり、要素はプリミティブまたは他のコンスのどちらかである。S式はコンスを実行時に連結して任意のパターンを構築する[[動的型付け]]の値である。コンスは要素二つの[[直積集合|直積型]](''product type'')であり、コンスの連結による要素の並びは[[線形リスト|リスト]]と呼ばれる。コンスの要素は形式化されていない[[非交和|非交和型]](''sum type'')でもあり、要素の識別はプログラマ側の裁量に委ねられている。コンスの組み合わせによるパターンは任意の識別名に結び付けられる。
 
[[代数的データ型]]は{{仮リンク|AND-OR木構造|en|And–or tree|label=}}のデータストラクチャ構造である。これは[[直積集合|直積型]](''product type'')または[[非交和|非交和型]](''sum type'')を表現する多項のデータ構築子の組み合わせで形成される。データ構築子は任意個数の要素を持つものであり、要素はプリミティブまたは他のデータ構築子のどちらかである。代数的データ型はデータ構築子を事前に組成定義して任意のパターンを構築する[[静的型付け]]の値である。直積型は[[タプル]]または[[構造体|レコード]]のパターンを表わす。レコードは指定フィールド取得用関数を随伴させたものである。非交和型は[[列挙型]]または[[共用体|タグ共用体]]のパターンを表わす。前者は等値性(''equality'')で識別される非交和である。後者は等価性(''equivalent'')で識別される非交和であり、こちらはユニオン型(''union type'')とも呼ばれる。ユニット型(''unit type'')は空集合のパターンを表わし、実装面ではnilまたはvoidの表現になる。ユニット型とそうでない型の二択の非交和型はオプション型(''option type'')とされ、実装面ではMaybe値の表現になる。データ構築子を再帰的にネスティングするパターンは帰納型(''inductive type'')とされる。非交和型と帰納型とユニット型の組み合わせは[[連結リスト]]や[[二分木]]のパターンを表わす。データ構築子の組み合わせによるパターンは任意の型構築子(''type constructor'')に結び付けられて同時にそれが識別名義になる。データ構築子がパターンの表現に用いられるのに対して、型構築子はパターン内の要素(プリミティブないしデータ構築子)の多相化に用いられる。多相化はパターン内の要素を型変数(''type variable'')に置き換え、型構築子への型引数(''type parameter'')で要素の型を決定するという形で行われる。型構築子が必要とする型引数の個数および形態によるパターンは[[カインド (型理論)|カインド]](''kind'')と呼ばれる。型構築子はカインドによって分類される。代数的データ型は識別名義と構成実装内容を分離して双方を自由に組み合わせるという意味でしばしば抽象化される。これは仮名義の型名(=型構築子)を用いてプログラムを記述しておき、プログラム冒頭でその仮実際の型構築子名を結び付け当てはめ仕組みもの実現されありその仮名義の定義は型シノニムまたは型エイリアスと呼ばれる。
 
=== 評価戦略 ===
47行目:
引数欄内関数の評価タイミング(''call-by-What'')には、値渡し(''call by value'')と名前渡し(''call by name'')がある。値渡しは先行評価に相当するものであり、関数の評価値が引数として渡される。名前渡しは遅延評価に相当するものであり、未評価のまま保留された関数が引数として渡される。なお、双方ともに引数確定されていない場合はただの第一級関数(関数の型の値)として渡されることになる。また名前渡しの亜流に必要渡し(''call by need'')があり、これは一度名前渡しされた関数+引数はその評価値を[[メモ化]]されて、同じ関数+引数が再度名前渡しされた時はそのメモ化評価値の方を渡すという仕組みである。必要渡しは純粋関数型言語で実装されている。
 
データストラクチャ構造でも遅延評価の概念は扱われており、[[帰納]]、[[再帰]]、[[無限]]、[[極限]]といった代数表現の実装手段になっている。代数的データ型では[[共用体|タグ共用体]]、[[線形リスト|連結リスト]]、[[再帰データ型]]は遅延評価対象である。連結リストは無限リストと構造上同義であり遅延評価が無限性質の実装を可能にしている。
 
=== 参照透過性 ===
[[参照透過性]](''referential transparency'')とは、関数は同じ引数値に対する同じ評価値を恒久的に導出し、その評価過程において現行計算枠外の情報資源に一切の影響を及ぼさないというプロセス上の枠組みを意味する。現行計算枠外のいずれかの情報資源要素が変化するのと同時にいずれかの関数の評価過程も変化してしまう現象が[[副作用 (プログラム)|副作用]](''side effect'')と呼ばれる。参照透過性=副作用の排除でもある。副作用を持たない関数を純粋関数(''pure function'')と呼ぶ。副作用が伴なう操作の代表例は値の再代入と入出力処理とシステムコールである。この参照透過性が完全順守されたは、副作用発生部分とそうでない部分を分けて処理構成の見通しを良くするなどのプログラム設計の視点から語られる事が多く、[[手続き型プログラミング|手続き型]]や[[オブジェクト指向プログラミング|オブジェクト指向]]ではその利点から解釈してよいものであるが関数型パラダイムではプロセス[[有向グラフ]]の整合性を維持するという視点から解釈する必要がある。プロセス有向グラフとはプログラム初期値からのあらゆる個体(値)と写像(関数)のつながりを表わす図式である。あらゆる値の変遷が初期宣言値まで遡れるといになる。宣言値から参照透過性が保証された関数型プログラムは、そあらゆる値をつなぐ写像[[正当性 (計算機科学)|正当性]]履歴[[形式的検証]]および[[数学的証明]]図表であ自動化が可能になりこれが本質的な利点になプロセス[[有向グラフ]]の解析と模型化は、[[プロセス代数]]と呼ばれ[[並行プログラミング]]などの支柱になる。関数型プログラミングの世界で値の再代入がタブーとされるのは、それが写像の言わば履歴の改ざんになってプロセス有向グラフの整合性を崩壊させるからである。従ってある時点の値をただ書き留めておく[[束縛変数]]と、旧値の更新を新値の産出生成で代替した[[イミュータブル]]が重視される。ループは関数の[[再帰]]で表現され、分岐は[[選言]][[パターンマッチング|パターンマッチングなどで表現される。参照透過性を維持しながら入出力処理を行うための機能には、派生構造型システム(''substructural type system'')]][[モナガード (プログラミング)|モナガード]](''monad'')で表現される。以上参照透過性の仕様上の意味である。
 
参照透過性が保証されたプロセス[[有向グラフ]]の実装、{{仮リンク|プルフアシスタディト|en|Proof asistant|label=}}による[[正当性 (計算機科学)|プロラム正当性]]の[[形式的検証]]および[[数学的証明]]を可能にする。参照透過性を完全順守するには、各種入出力に伴うレベルで副作用の論理的排除も必要なの専用きる部分へランタイム環境上対応と、そうはない部分へ動作が必須対応大別される。ランタイム環境前者「コンテキスト」再代入など走行指しプログラムとの仲介委ねられる問題になる。プログラ後者は入出力処理やシステコールなどを指しこちらでそうはいかない。後者の参照透過性は、副作IDを内包す原因になコンテキスト全ての情報要素を関数の引数収納し、副作用結果になった全ての情報要素を関数の返り値に収納するという形式で各種入出力を行う表現される情報要素には理論上は関連するコンテキストへの仮想的入出力は、ランピューイム環境側によっ状態が全実際に実行内包されてい事になるが、その入出力忠実な実践は当然不可能なの、コンピュータ環境状態を抽象化したランタイム「抽象環境が反映」で代用された新生IDを内包す。抽象環境は言語処理系から提供される特殊データである。この抽象環境を関数の引数とその返り値に常時含めて、ーディテキストがグレベルの副作用排除はプログラム側に渡マが解決する事で理論上は参照透過性が順守されている事になる。コンテキスこの論理的なIDリック方式毎時ユニ参照透過性の実装上の意味である。抽象環境を用いて参照透過性を維持しつつ再代入/入出力/システムコルなどを行うための機能には、{{仮リン|派成され構造型システム|en|Substructural type system}}と[[モナド (プログラミング)|モナド]]があ仕組みは、派生構造型システムは、抽象環境をライナ型(''linear type'')と呼ばにして関数に渡される。コンテキストその中のIDデータをユニーク更新させる。抽環境にユーザーデータを注入する仕組みはアフィン型(''affine type'')、コンテキスト抽象環境から対象値ユーザーデータを抽出する仕組みは関連型(''relevant type'')と呼ばれる。毎時ユニーク生成更新されるライナー型IDデータは、各種入出力に伴う副作用によって実際には変化しているピューイム環境の時系列状態を完全に抽象化して、それらを理論上各個照会可能にしているマッピングキーである。これによってピューイム環境状態の変化もプロセス有向グラフで論理的に辿れることになるので同時に参照透過性も維持されていることになる。派生構造型システムの実装例には{{仮リンク|ユニークネス型(''uniqueness|en|Uniqueness type'')}}がある。ライナー型、アフィン型、関連型を組み合わせての特にその応用コーディングは煩雑なボイラープレートコードになりがちだったので、それらを[[圏論]]視点の[[関手代数的構造]]由来合成といった仕組みアイディアでより平易かつ簡潔にした手法が[[モナド (プログラミング)|モナド]]である。
 
=== 型システム ===
60行目:
'''静的型付け'''
 
関数型言語の静的型付けでは、性質や役割による[[セマンティクス|意味づけ]]によって値を分類する明示的型付け(''manifest typing'')よりも、計算可能性に基づく[[等価性]]によって値を分類する推論的型付け(''inferred typing'')が主流である。前者の意味づけとはプログラマによる型定義、型宣言、型注釈を指しており人間寄りの視点である。後者の等価性とは値を関数/演算子の引数にできるかどうかの判別を指し、値への関心がそこで計算可能かどうかに絞られているので計算機寄りの視点である。明示的型付けではソースコード上の型宣言と型注釈から値の型が特定されるのに対し、推論的型付けでは[[型推論]]機能で特定される。型推論とは専用のアルゴリズムによる解析によってソースコード解析によってあらゆる値/変数/引数/返り値それぞれの等価性を導き出す機能である。数値や文字列といったリテラルはそのまま特定され、変数などのシンボルはその扱われ方や、任意の[[等式]]を並べて定義した法則からの分析によって型(=等価性)が特定されるといった具合である。推論的型付けでは値への関心をその計算可能性に絞っているので、型宣言と型注釈はとさになりまたは相容れないものとなる。例としてint型を型シノニムで金額型と数量型にした場合、明示的型付けではこの両者は区別されるが、推論的型付けでは区別されない。ソースコードの解析でどちらもint型準拠の等価と見られるからである。推論的型付けで値の意味づけ性も表現する場合は、データ構築子で値を包む[[ボックス化]]が用いられる。データ構築子(''data constructor'')は与えられた要素を直積または非交和でまとめるのと同時に[[型理論]]で言われる文脈(''context'')を各要素の等価性に上乗せ付加するものでもある。
 
静的型付けにおける[[データ構造|データストラクチャ]]のパターン(型)はコンパイル前ないし実行前に全て事前形成される。その実装例である[[代数的データ型]]はデータ構築子の組み合わせでパターンを構築し、パラメトリック多相に基づいて[[ジェネリックプログラミング|総称化]]したパターン内の要素=型変数を、型構築子への型引数の組み合わせで特定した。''Hindley–Milner''型体系はこのパラメトリック多相に対応した[[型推論]]機能を提供している。型構築子(''type constructor'')は必要とする型引数の個数によって分類され、これは[[カインド (型理論)|カインド]](''kind'')と呼ばれる。カインドは総称記号である<code>*</code>の写像で型構築子の型種を表現する。型引数を必要としない型構築子と必要な型引数を全て付与された型構築子はプロパータイプと呼ばれ<code>*</code>と表現される。プロパータイプは[[全称量化子|全称量]]の型である。型引数を1個必要とするものは<code>*→*</code>になり、2個必要なら<code>*→*→*</code>になる。これらは[[存在量化子|存在量]]の型になる。<code>*→*→*</code>に型引数が1つ付与されると<code>*→*</code>になり更に1つ付与すると<code>*</code>のプロパータイプになる。全称量の型付け値(ターム)は普通に扱えるが、存在量の型付け値はその一部分が抽象化(大抵は環境依存値と同義)されたままの特別な値と見なされて一定の制限下で扱われる。
 
推論的型付け下の関数の扱いでは、人為的表記による意味づけを重視した記名的型付け(''nominal typing'')が取り入れられており、これで推論的型付けの枠組み内での[[多重定義|関数オーバーロード]]が表現されている。ここでの人為的表記による意味づけとは、型構築子/データ構築子/’’関数の型’’それぞれのパターン内の型変数に、[[型理論]]で言われる文脈(''context'')を付加することを指している。文脈の付加は制約(''constraint'')と呼ばれる。文脈の付加はアドホック多相と考えられており、代表的な実装例はそれと[[ジェネリックプログラミング]]を組み合わせた[[型クラス]]である。型クラスは、引数/計算値/評価値などを[[総称型]]化したジェネリック関数群の’’関数の型’’と式内容を定義できる機能であり、同時に推論的型付けと共存する関数オーバーロードの実装と、特定の意味づけ型を扱うための関数モジュールを定義するための手段になっている。型クラスの定義構文では上述のジェネリック関数群が定義され、その型クラス名が文脈記号になる。型構築子の定義に文脈を付加すると、その型クラスのジェネリック関数群にその型構築子=型を当てはめた関数群がコンパイル時に自動生成される(deriving)。また文脈を付加して当てはめ関数群を自動生成(instance)した上で、その型構築子=型のための当てはめ関数の式内容を個別定義(where)できる構文もある。この双方がジェネリック関数の特有インスタンス化になる。明示的型付けでは型注釈を付けた引数パターンの列挙というシンプルな手段で関数オーバーロードを表現できるが、推論的型付けでは等価性に上乗せした文脈という二段階の手段が必要になる。記名的型付けと併せた推論的型付けでのオーバーローディング関数の選択決定は、始めに仮引数と実引数の型クラスのみに注目した照合が行われ、次にその型クラスの制約内での型推論照合が行われるという形になる。
 
'''動的型付け'''
 
動的型付けにおける[[データ構造|データストラクチャ]]のパターン(型)はコンパイル前ないし実行前の事前形成に加えて、実行中の随時にも事後形成できる。その実装例である[[S式]]は、二項データ構築子([[Cons (Lisp)|コンス]])の実行時の連結で形式化されていないパターンを構築し、プログラマの裁量による任意の実行時チェックでパターンの意味づけと計算に用いるための等価性を判別するといったものである。パターンの組み合わせが明確に形式化されておらずその識別をプログラマの裁量に委ねており、またチェックタイミングも委ねられている事から、これは潜在的型付け(''latent typing'')と呼ばれている。潜在的型付けは動的型付けの原型的位置づけである。
 
もう一つの実装例として動的なレコード(''record'')がある。この動的レコードは内部的には[[動的配列]]と同じものであり、配列の各スロットに任意の値の[[参照 (情報工学)|参照]]が納められて、そのスロットは[[構造体]]のフィールドと同じものになる。スロットは増設削減可能である。レコードのタグ名はそのまま型名になる。レコードを量化した値([[インスタンス]])には、システム側が別途用意する型情報が結び付けられており、変数への束縛ないし代入および関数/演算子への引数代入時に毎回自動的に型判別される。型情報と型判別タイミングが形式化されているのでこれは動的型付けとなる。動的型付けのインスタンスを関数の引数にすることは動的な[[多重定義|関数オーバーロード]]を自然表現し、これはオブジェクト指向に倣って[[多重ディスパッチ]]とも呼ばれる。レコード・フィールドのアクセスは、フィールド名関数をインスタンスに適用するという方法で行われる。オブジェクト指向の<code>instance.field</code>が関数型では<code>field instance</code>のようになる。同じフィールド名関数から得られる値の型は、適用するインスタンスの型構造による実行時多態になる。この仕組みは構造的型付け(''structural typing'')に沿ったものである。
86行目:
モナド値は付加モナドと自由モナド(Maybe/例外/有限リストモナドなど)以外では、実質的に存在量の値になるので普通の値のように扱うことは出来ない。ただし付加/自由モナドでも参照透過性を維持するためには存在量と同等に扱う必要が出てくる。<code>return</code>を基本値に適用してモナド値を表現することからモナド処理は始まる。基本値とは扱うモナドに合わせた任意の値である。そのモナド値は圏としてのユニークIDを持つことになる。ここで基本値を<code>A</code>としそのモナド値を<code>MA</code>とする。<code>MA</code>にbindを適用して<code>(A→MB)→MB</code>という写像を導出する。その写像は先の<code>MA</code>と同じ圏IDを備えたものになる。その写像をモナド関数<code>(A→MB)</code>に適用すると、そのモナド関数内では渡された<code>A</code>やその他の値などに<code>return</code>を適用して表現されるモナド値の圏IDは<code>MA</code>のもので共通化される。モナド関数内においての<code>return</code>は基本値をモナド値に代入する機能と見てよい。<code>return</code>は用途別関数にそれぞれラッピングされて使われるのが普通である。空引数からモナド値を表現する<code>return</code>もありこれはモナドプリミティブ(''monadic primitive'')と呼ばれ、この場合はモナド値の圏IDが暗黙引数になっている。モナド値はファイルハンドルのようなものと考えると分かりやすくなり、モナド値を直接引数にできる専用関数も存在する。モナド関数内ではモナド値から基本値を取り出す演算子が有効になる。それは<code>A←MA</code>のように表現されてコモナド(''comonad'')と呼ばれる機能になる。抽出した基本値からの処理の中で再度<code>return</code>が行われる。モナド関数は自己関手内容に見立てられているので、その中では<code>return</code>の繰り返しによる事実上の再代入処理が許されている。その論理的な辻褄合わせの要点になる<code>bind</code>の正当性および計算可能性を表現するためにファンクタ則とモナド則の等式がプログラム内で定義されている。ここでいわゆる圏論の知識が必要になるがその説明は先送りする。モナド関数はモナド値を返しそれに再度<code>bind</code>を適用できるのでこれがモノイドを意味している。モナド関数の外での<code>return</code>は毎時ユニークな圏IDのモナド値を表現するので同じ基本値でもその都度異なる圏が表現される事になり、これが[[自然変換]]([[関手圏]]の[[射 (圏論)|射]])演算子の呼称由来になっている。
 
モナドは'''ファンクタ'''(''functor'')の派生文脈にされることが多いが、これは<code>bind</code>を形成するクライスリ射と<code>join</code>の合成の持ち上げ(関手)に<code>fmap</code>が使われるからである。ファンクタ文脈は関手<code>fmap</code>を持つ。そのままファンクタの機能名で呼ばれることが多い<code>fmap</code>は、関数<code>(A→B)</code>から関数<code>(TA→TB)</code>を導出する関手=関数である。この関数は<code>TA</code>に適用できて<code>TB</code>を導出できる。<code>T</code>は基本値を包むコンテキストまたはコンテナでありその代表例はリストである。基本値に対する作用をコンテキストで拡張解釈できるのがファンクタの利点である。例えば基本値への+1という作用をリストのコンテキストで拡張解釈するのはリストの全要素に+1するという意味になる。これはリストをそのまま計算対象にできる利便性に繋がる。ファンクタの派生文脈に'''アプリカティブ'''(''applicative'')がある。アプリカティブは、ファンクタのコンテキストに包まれた関数を「コンテキストに包まれた先頭引数→コンテキストに包まれた残り引数&評価値」という1引数の関数に変換する演算子<code><*></code>を持つ。<code><*></code>は<code>F(A→B)</code>から<code>(FA→FB)</code>を導出する演算子である。<code>F</code>はコンテキスト、<code>(A→B)</code>は元の関数、<code>(FA→FB)</code>は1引数の関数である。この<code>FB</code>は多相であり実際は値<code>F*</code>関数<code>F(*→*)</code> 関数<code>F(*→*→*)</code>などになっているのでそれに<code><*></code>を再び適用できる。アプリカティブは、2個以上の引数の関数をファンクタするための機能と考えてよい。2個以上の引数の関数は<code>fmap</code>でそのまま持ち上げられないのでアプリカティブ関手の<code>pure</code>が用いられる。これは<code>A→FA</code>と表現され<code>A</code>が関数、<code>F</code>がコンテキストである。<code>pure</code>によって好きな引数個数の関数をコンテキストで包み<code><*></code>をそれに適用して導出された関数を<code>FA</code>に適用できる。アプリカティブ関手<code>pure::A→FA</code>とモナドのη自然変換演算子<code>return::A→MA</code>は同じ働きに見えるが、前者は関数(関数の型の値)を純粋に持ち上げるだけの[[関手]]なのに対して、後者は持ち上げる関手を毎時垂直合成していく[[関手圏]]の[[射 (圏論)|射]][[自然変換]]であるという性質上の違いがある。
 
== 歴史 ==
109行目:
'''1990年代'''
 
1990年に関数型プログラミングの第二のマイルストーンと言える純粋関数型言語「[[Haskell]]」が初リリースされた。Haskellは遅延評価と型理論の文脈を形式化した型クラスと圏論由来のデザインパターンであるモナドの導入を特徴にしていた。1992年に[[動的型付け]]レコードと[[多重ディスパッチ]]を扱う関数型言語「[[Dylan]]」が登場した。1993年に[[ベクトル]]、[[行列]]、[[表 (データベース)|表テーブル]]などのデータストラクチャ構造を扱えて[[統計的検定]]、[[時系列分析]]、[[データ・クラスタリング|クラスタリング]]分野に特化した関数型言語「[[R言語|R]]」が発表された。1995年にLISPの[[マクロ (コンピュータ用語)|マクロ]]機能を大幅に強化したコンポーネント指向により各分野に合わせた[[ドメイン固有言語]]として振る舞える「[[Racket]]」が登場した。1996年にはML方言のCamlにオブジェクト指向視点の[[抽象データ型]]を導入した「[[OCaml]]」が公開された。90年代の関数型プログラミングの歴史では関数の数学的純粋性に則った[[参照透過性]]の重視の他、[[オブジェクト指向プログラミング|オブジェクト指向]]との連携の模索が目立っていた。日本ではStandard MLに独自の拡張を施した「SML#」が発表されている。風変りなものに[[コンビネータ論理]]の形式に立ち返った「[[Unlambda]]」がある。1995年に公開された「Mercury」は関数型と[[論理型プログラミング|論理プログラミング]]の合の子のような言語であり、[[自動推論]]分野への応用に特化されていた。
 
'''2000年代'''
 
2000年代になると関数型プログラミングへの注目度は更に高まり、マルチパラダイムに応用された関数型言語が様々に登場した。2003年のJava仮想マシン動作でオブジェクト指向と関数型を融合した「[[Scala]]」、2005年のマイクロソフト製のML方言「[[F Sharp|F#]]」、2007年のJava仮想マシン動作のLISP方言「[[Clojure]]」など数々のポピュラー言語が生み出されている。また、[[カリー=ハワード同型対応|カリー=ハワード同型対応]]の理論に基づいた{{仮リンク|プルーフアシスタント|en|Proof asistant|label=}}によるプログラム正当性の数学的証明を指向した関数型言語が支持され、2004年に「Epigram」2007年に「[[Agda]]」および純粋関数型「Idris」が発表されている。これらの言語では数学者[[ペール・マルティン=レーフ|マルティン・レーフ]]による{{仮リンク|直感的型理論|en|Intuitionistic type theory}}準拠のに基づく[[依存型]]を中心にした型システムが実現されている。関数型構文の有用性がより広く認識されるに従い、オブジェクト指向言語やスクリプト言語にも積極的に導入されるようになった。産業分野からも注目されるようになり、[[Constructive Solid Geometry|CSG]]幾何フレームワーク上で動く[[CAD]]への導入も始められた。しかし関数型コンセプトに馴染まないオペレーターが定数化規則による値の再代入制限に困惑して設計作業に支障をきたすなどの弊害も明らかになっている。
 
== 代表的な関数型言語 ==
154行目:
'''[[Erlang]]''' (1986年)← LISP、[[Prolog]]、[[Smalltalk]]
 
: 動的型付け、先行評価、並行計算
 
'''[[Clean]]''' (1987年)← Miranda
182行目:
'''[[Scala]]''' (2003年)← Scheme、Standard ML、Haskell、Erlang、[[Smalltalk]]、[[Java]]
 
: 静的型付け、先行評価、オブジェクト指向、並行計算
 
[[F Sharp|'''F#''']] (2005年)← Standard ML、Haskell、Erlang、Scala、[[Python]]、[[C♯]]
 
: ML方言、静的型付け、先行評価、並行計算
 
'''[[Clojure]]''' (2007年)← Scheme、Haskell、Erlang、[[Java]]
 
: LISP方言、動的型付け、先行評価、並行計算
'''[[Rust (プログラミング言語)|Rust]]''' (2010年)← Scheme、Standard ML、Haskell、Erlang、[[C♯]]
: 静的型付け、先行評価、並行計算
 
== 関数型プログラミングの例 ==
234行目:
 
== 脚注 ==
{{脚注ヘルプ}}'''注釈'''{{Notelist}}'''出典'''{{Reflist}}
=== 注釈 ===
{{Notelist}}
=== 出典 ===
{{Reflist}}
 
== 外部リンク ==
* [http://www.sampou.org/haskell/article/whyfp.html なぜ関数プログラミングは重要か]
* [http://www.topxml.com/xsl/articles/fp/ {{lang|en|The Functional Programming Language XSLT - A proof through examples}}]
 
 
{{Normdaten}}
{{プログラミング言語の関連項目}}