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

削除された内容 追加された内容
SML 言語族に関する説明を少し強化
Fumiexcel (会話 | 投稿記録)
不適切、あるいは冗長な記述を削除。大部分の記述を修正。ScalaはOOPとFPの両方を売りにした言語で、関数型プログラミング言語に分類しても差し支えない。
1行目:
{{独自研究|date=2014年4月}}
{{プログラミング言語|index=かんすうかたけんこ}}
'''関数型言語'''(かんすうがたげんご、functional language)は、'''関数型プログラミング'''に向いた特徴を持つ[[プログラミング言語]]、関数型プログラミング言語(functional programming language)の略称である。引数に関数を作用(applicate)させて計算をおこなうことから、作用型言語(applicative programming language)ともいう。[[データフロープログラミング]]言語も関数型言語の一種である<ref name="iwanami-is-dic">『岩波情報科学辞典』pp. 138-139</ref>
 
== 関数型プログラミング ==
関数型プログラミングは、理論的な計算モデルとして[[ラムダ計算]]や[[項書き換え]]を基礎とする。そのため、関数は[[第一級オブジェクト]]として扱えることが必要となる。
ここでの「関数」とは、数学でいう「[[関数 (数学)|関数]]」である。[[手続き型プログラミング]]などにおける「[[サブルーチン|関数]]」も同じ機能を持つ。原則としては[[副作用 (プログラム)|副作用]]がないものを関数と呼ぶ。副作用がある関数を認めるかどうかは、プログラミングで何がしたいかによる。空間的には副作用がないという関数も、瞬時に計算を終わることはできず、時間を浪費するという時間軸での副作用がある。時間と空間を含めた関数について考えるとよい。数学では時間を考慮しないが、プログラミングでは時間を考慮する必要がある。空間的な副作用がない関数、空間的な副作用がある関数という具合に厳密に分類するとよい。システム理論では、入力を出力に変換するものをシステムと呼び、関数と同じ機能を持つ。システムでは、出力だけある湧き出し点、入力だけあるブラックホールを仮設としてシステムに含めている。関数も、システム理論に習い、出力だけある関数、入力だけある関数も含めて、広義の関数理論を形成するとよい。
 
関数への引数がプログラムへの入力で、関数を引数に作用させて評価して得られる値がプログラムからの出力であるとすると、コンピュータプログラムはある種の関数であると考えることができる。ここで、入力や出力は記憶装置中のファイルのようなものばかりではなく、マウスの動きの情報といった入力や、画面への表示といった出力も考えられ、関数型プログラミングにおいては実際にそれらを扱う関数としてモデル化する。
Pascalでは「手続き」と呼ぶ値を返さないルーチンもC言語では「関数」と呼ぶ。C言語の値を返さない言語は、void(何もないもの)を返すという記述をしている。0が数であるのと同様、voidも型であり、void型を返すと理解するとよい。Pascalは手続き型言語で、C言語は関数型言語というのは明らかに誤りである。<ref>[[共立出版]]『ANSI C/C++辞典』ISBN 4-320-02797-3 など</ref>。
 
関数への引数がプログラムへの入力で、関数を引数に作用させて評価して得られる値がプログラムからの出力であるとすると、コンピュータプログラムはある種の関数であると考えることができる。ここで、入力や出力は記憶装置中のファイルのようなものばかりではなく、マウスの動きの情報といった入力や、画面への表示といった出力も考えられる。極端には、一般的な[[機械語]]の命令も、レジスタやメモリのようなコンピュータの内部状態の、命令実行前の全状態を入力とし命令実行後の全状態を出力とする関数である。これはシステム理論と同じ考え方である。ここで「全状態」としているのは、入出力の線をどこで引くかによる。ある時間の全状態を入力とし、次の時間の全状態を出力とすれば、BASIC言語の <code>poke()</code> 関数によるメモリの書き換えは、メモリの状態は引数にも返す値にも含まなないが、メモリの状態そのものを全状態に含むため、副作用による作用ではなく関数である。全状態を出力に含めなければ、引数にも返す値にも含まない <code>poke()</code> 関数は全状態を考えない場合の「関数」ではない。
 
複雑なコンピュータプログラムに相当する関数を作るために、[[第一級関数]]を扱えることが関数型言語に有用である。概要の節で詳説する。
 
他の[[プログラミングパラダイム]]との関連としては、関数型プログラミングは[[論理プログラミング|論理型プログラミング]]と同じく[[宣言型プログラミング]]・[[非手続き型言語|非手続き型プログラミング]]で、[[手続き型プログラミング]]・[[命令型プログラミング]]と分類を異にする。
 
== 概要 ==
関数型プログラミング言語は、基本的に関数型プログラミングのために、[[第一級オブジェクト]]であり、関数]]を定義に対する。[[カリー化]]などの機能を持つ。静的型付けの言語は[[型推論]]の機能がある。理論的な計算モデルとし操作によっは[[よりプログラムダ計算]]や[[項書き換え]]基礎と構築する手段を提供する。
 
純粋関数型プログラミング言語では、[[参照透過性]]が常に保たれるという意味において、全ての[[式 (プログラミング)|式]]や関数の評価時に[[副作用 (プログラム)|副作用]]を持た生まない。入出力などのために、多くの純粋関数型プログラミング言語であるHaskellやClean正格性に関な評価を基本として非正格であり、引数はデフォルトで[[遅延評価]]であされる。一方、[[Idris]]は純粋だが正格評価を採用している。また入出力などを[[参照透過性]]を保ったまま実現するために、たとえば[[Haskell]]では[[モナド (プログラミング)|モナド]]、[[Clean]]では{{仮リンク|一意型|en|Uniqueness type}}という特殊な型を通して一貫性み入出力などある表現扱えるようにしてい提供する。
 
非純粋関数型言語では、参照透過性を壊す、副作用があるような式や関数も存在する。Lispなどでデータ構造の破壊的変更などの副作用を多用したプログラミングをおこなうと、それはもはや手続き型プログラミングと同じである。多くの場合、非純粋関数型言語は多くが正格性に関して正格であり、[[評価戦略]]としては正格評価(先行評価)である。無限リストのような技巧を使うために明示して遅延評価する部分明示するとで、無限リストわせどを扱えため機構を持つもある
 
[[JavaScript]]や[[Java]]など近年の[[高水準言語]]には、関数型言語の機能や特徴を取り入れているものがある。変数の値やオブジェクトの状態を書き換えるプログラミングスタイルを通常とするため、関数型言語とは分類されない。一方[[LISP]]は、その多くが副作用のある式や関数が多数あり、手続き型スタイルでのプログラミングも可能だがされることが多いが、理論的なモデル(「[[純LISP]]」)の存在や副作用を使わないプログラミングが基本であること、歴史的理由などから、関数型プログラミング言語に含む。Pascalでは「手続き」と呼ばれる値を「返さない」ルーチンも、C言語ではvoid型の値を返す関数と捉える。しかし、Pascalは手続き型言語で、C言語は関数型言語というのは明らかに誤りである<ref>[[共立出版]]『ANSI C/C++辞典』ISBN 4-320-02797-3 など</ref>。なお、[[OCaml]]や[[Haskell]]などでは、「自明な値(例えば<code>()</code>)を返す」と、値を返さない(Voidなど)はより明確に区別され、後者は停止しないか例外を出す(そのため結果がない)ようなプログラムを表す
純粋関数型プログラミング言語では、[[参照透過性]]が常に保たれるという意味において、全ての[[式 (プログラミング)|式]]や関数は[[副作用 (プログラム)|副作用]]を持たない。入出力などのために、多くの純粋関数型プログラミング言語は正格性に関して非正格であり、引数はデフォルトで[[遅延評価]]である。また入出力などを[[参照透過性]]を保ったまま実現するために、たとえば[[Haskell]]では[[モナド (プログラミング)|モナド]]、[[Clean]]では{{仮リンク|一意型|en|Uniqueness type}}という特殊な型を通してでのみ入出力などを扱えるようにしている。
 
なお、「関数型言語である」と「関数型プログラミングをする」は同値ではなく、関数型には分類されない言語で関数型プログラミングをすることや、関数型プログラミングを基本とする言語の上で他のパラダイムを実現する例もある<ref name="Novatchev">{{cite web | url = http://arxiv.org/abs/cs/0509027 | author = Oleg Kiselyov, Ralf Lämmel | title = Haskell's overlooked object system | accessdate = Sep 10, 2005}}</ref>。
非純粋関数型言語では、参照透過性を壊す、副作用があるような式や関数も存在する。Lispなどでデータ構造の破壊的変更などの副作用を多用したプログラミングをおこなうと、それはもはや手続き型プログラミングと同じである。非純粋関数型言語は多くが正格性に関して正格であり、[[評価戦略]]としては正格評価(先行評価)である。無限リストのような技巧を使うために、明示して遅延評価をおこなわせるための機構を持つ。
 
[[データフロープログラミング]]言語も関数型言語と共通した特徴を部分的に持つ。
[[JavaScript]]など近年の[[高水準言語]]には、関数型言語の機能や特徴を取り入れているものがある。変数の値やオブジェクトの状態を書き換えるプログラミングスタイルを通常とするため、関数型言語とはしない。一方[[LISP]]は、その多くが副作用のある式や関数が多数あり、手続き型スタイルでのプログラミングも可能だが、理論的なモデル(「[[純LISP]]」)の存在や副作用を使わないプログラミングが基本であること、歴史的理由などから、関数型プログラミング言語に含む。
 
== 歴史 ==
[[FORTRAN]]には、関数(ここでは副作用のあるものを含む意味で)を定義できる、「ADD A, 1」のような形ではなく「A = A + 1」のように式で計算を指示できる、といった特徴もあったが、基本的には手続き型プログラミング言語であった。
 
[[LISP]]は、その基本機能のモデルとして関数型の[[純LISP]]を持つなどといった特徴がある、最初の関数型言語である。今日広く使われているLisp方言のうち特に[[Scheme]]は関数型としての特徴が強い。
 
現代的な関数型プログラミング言語の祖としてはアイディアが1966年に発表された[[ISWIM]]が挙げられるが、1970年前後までは関数型プログラミング言語の歴史はLispの発展が主である。1970年代にプロジェクトが開始された[[w:Logic for Computable Functions]]のための言語として[[ML (プログラミング言語)|ML]]が作られている。
32 ⟶ 31行目:
またLISPにおいて、変数のスコープに静的スコープを採用した[[Scheme]]が誕生したのが1975年である。
 
1977年、FORTRANの設計と[[バッカス・ナウア記法|BNF]]の発明の業績でこの年の[[チューリング賞]]を受賞した[[ジョン・バッカス]]は、''Can Programming Be Liberated From the von Neumann Style?: A Functional Style and Its Algebra of Programs''<ref>「プログラミングはフォン・ノイマン・スタイルから解放されうるか?: 関数型プログラミング・スタイルとそのプログラム代数」、[[米澤明憲]]訳『ACMチューリング賞講演集』([[共立出版]])pp. 83-156</ref>と題した受賞記念講演で関数型プログラミングの重要性を訴えた。講演では[[w:FP (programming language)|FP]]という関数型プログラミング言語の紹介もした(サブタイトルの後半の「プログラムの代数」はこれを指す)が、これは[[APL]](特に、[[高階関数]]の意味がある記号(APLの用語ではoperator([[作用素]])という))の影響を受けている(APL自体はふつう関数型とはされない)
 
バッカスのFPは広く使用されることはなかったが、この後関数型プログラミング言語の研究・開発は広まることとなった。1985年に[[Miranda]]が登場した。1987年に、遅延評価の純粋関数型プログラミング言語の標準の必要性が認識され[[Haskell]]の策定が始まった。1990年にHaskell 1.0仕様がリリースされた。同じく1990年にはMLの標準である[[Standard ML]]もリリースされている。
 
[[Clean]]は1987年に登場したが、発展の過程で[[Haskell]]の影響を受けている。1996年に、ML処理系のひとつであったCamlに[[オブジェクト指向]]を追加した[[OCaml]]が登場した。また日本ではSMLに独自の拡張をほどこした[[SML#]]が開発されている。
 
21世紀に入ると、[[Java仮想マシン]]や[[共通言語基盤|CLI]]をランタイムとする関数型プログラミング言語を実装しようという動きがあらわれ、[[Scala]]・[[Clojure]]・[[F Sharp|F#]]などが登場した。
 
== 代表的な関数型言語 ==
=== 純粋関数型言語 ===
* 強い静的型付け
** [[Clean]]
** [[Haskell]]
** [[Miranda]]
** [[Idris]]
* 型なし
** [[Lazy K]]
 
=== 非純粋関数型言語 ===
* 強い静的型付け
** [[プログラミング言語ML|ML]]
55行目:
** [[OCaml]]
** [[F Sharp|F#]]
** [[Scala]]
* 動的型付け
** [[Erlang]]