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