宣言型プログラミング

宣言型プログラミング: Declarative programming)は、総称的に用いられるプログラミングパラダイムであり、プログラミング言語およびパラダイム間の分類用語として使われることが多い。対義語である命令型プログラミングとの対比概念としてよく用いられる。宣言型はプログラミング理論のバックボーンである数学および形式論理学の性質に準拠したものであり、オートマタ理論的には決定性チューリングマシンに沿っている。前提と結論、定義と推論、問題と解答、条件と結果、入力と出力などが時系列に関係なく正確に対応して例外なく明示されており、その触媒となる作用、写像、演算などは原則として冪等であり、それに対する入力要素と出力要素が不可欠になっているという性質を指して宣言的(declarative)または平叙的(declarative)と言われる。

概略すると「一つのトランザクションまたは一つのプログラム実行枠内でのあらゆるオペレーションにおいて、その遷移作用原因になる全情報要素と全環境状態をオペレーションのインプットに内包し、その遷移作用結果になった全情報要素と全環境状態をオペレーションのアウトプットに内包して、遷移作用の法則が不変になっている」というプロセス全般または作業全般を指して宣言的と言われる。遷移作用(transition function)の冪等性によるインプットとアウトプットの対偶関係は参照透過性を表わす。サブルーチン処理内容に重点を置いている命令型に対して、宣言型は引数と返り値に焦点を当てているとも言える。

来歴

宣言型プログラミングは、プログラミング言語のための分類用語としての性格が強い総称的なプログラミングパラダイムなので、どうしてそのような分類が必要になったのかという視点から解釈するのが理解への早道になる。宣言型と命令型という二大分類が定義された背景には、数学や形式論理学数理論理学)をバックボーンにしたコンピュータ原理とプログラミング言語理論の成立過程がある。あらゆる問題への解法において、数学や形式論理学の性質を引き継いでいる従来の方式が宣言型であり、計算機科学が持つようになった特有の方式が命令型である。宣言型は問題と解答の二者限定の平叙な関係の計算であるのに対して、その関係に状態(state)というものが加えられた計算の方は命令型となった。”状態”は問題内で宣言されていない潜在要素であり、命令型の計算は同じ問題でもその時の”状態”によって解答が変化した。計算過程による”状態”の変動とそれによる後続計算への影響は副作用と呼ばれる。”状態”の影響が無い平叙な計算と、”状態”に影響される手続き的な計算はコンピュータ計算性質の根本的な分かれ目になったので、前者の宣言型と後者の命令型というプログラミング理論上の二大分類が生まれた。

オートマタ理論的には、計算は遷移関数(transition function)、問題は入力値、解答は出力値、状態は遷移関数の非決定性に読み替えられる。プログラム的には、計算はサブルーチン、問題は引数、解答は返り値、状態はサブルーチン内外の静的メモリデータに読み替えられる。状態による計算の複雑性の拡充は、ノイマン型のコンピュータ機構に適したものだったので機械語低水準言語アセンブラ)はその必要性から命令型で構築された。また最初期の高水準言語FORTRANCOBOLALGOL)もノイマン型の性質に適した命令型になった。1950年代半ばからの高水準言語の普及につれて、命令型が利点的に拡充した計算の複雑性が今度は問題として挙げられるようになり、1960年代になると数学への原点回帰とも言える宣言型がその解決策として注目され始めた。また並行計算と宣言型の適性も認識され始めた。1970年代になると宣言型は非ノイマン型コンピュータ構築のためのパラダイムとしても重視されるようになった。

FORTRANALGOLCOBOLといった命令型imperative高水準言語が公開されていく中で、1958年に初回発表された「LISP」が宣言型(declarative)高水準言語の元祖と見なされている。LISPは自動推論自然言語処理自動定理証明人工知能研究といった目的で使われたが、当時の数学者の視点では命令的=手続き的(procedural)な言語と解釈されていた。式再帰の多用による複雑系のアルゴリズムがもはや平叙的(declarative)ではないと見なされたからである。1960年代からメインフレーム上の並行計算データフロープログラムが普及しそのための低水準言語はより平叙的な性質を示していた。また述語論理を基礎にした論理プログラミングの研究も始まりその中で実装された数々の言語もより平叙的な性質を持っていたが、1969年稼働の「Planner」は手続き的性質を備えていた。1964年公開の動的型付け関数型言語「APL」は平叙的であったが、LISPを原点にした60年代の関数型プログラミングは概ね手続き的性質に寛容だった。1970年代になると平叙的な静的型付け関数型言語「ML」と純粋関数型の先駆である「SASL」が公開され、1977年発表の関数水準言語「FP」は明確な平叙的=宣言型を旨としていた。これを発表したジョン・バッカスFORTRAN開発者)は、関数型スタイルを従来のノイマン型コンピュータからの脱却と飛躍に結び付けた。命令型に対する宣言型というプログラミング理論構図が広まったのはこの頃からである。

特徴

宣言型プログラミングとは端的に述べると、あらゆる遷移関数(transition function)において、状態遷移の原因になるあらゆる情報要素とあらゆる環境情報を遷移関数の引数に収納しており、状態遷移の結果になったあらゆる情報要素とあらゆる環境情報が遷移関数の返り値に収納されており、それら引数と返り値と遷移関数の整合性を維持するために、同じ引数に対する遷移関数の処理内容と返り値の一定化が保証されているというプロセス全般の構築を意味している。これらの仕様は主に参照透過性というプログラム概念に結び付けられている。これらの仕様を実現するための実装スタイルを箇条書きすると大抵は以下のようなものになる。

  • 関数へのパラメータ引数に状態遷移の原因になるあらゆる情報要素と環境要素が内包されており、同関数からのリターン値に状態遷移の結果になったあらゆる情報要素と環境要素が内包されている。
  • 同じパラメータ引数に対する関数の処理内容は常に同一であり、そのリターン値も常に同一である。
  • 関数の実行には原則的にパラメータ引数の入力を必要とする。入力の無い関数は上述の状態と副作用に依存した稼働方式になるからである。
  • 実行された関数は原則的にリターン値の出力を要求される。出力の空白は次の関数への入力の喪失を意味するのでプロセス停止に繋がる。
  • をプログラムの基本単位とする。式は関数または演算子をノードにした入力と出力の連鎖体であり、式自体が最終出力値と同義になる。式は初期入力値を与えられて計算実行される。この計算実行は評価と呼ばれる。初期入力値は引数と呼ばれる。最終出力値は評価値と呼ばれる。
  • 初期入力値を与えられた式はその計算実行を保留する事もできる。その保留状態の式の任意時の計算実行は遅延評価と呼ばれる。
  • 保留状態の式を値として他の式の初期入力値にする事もできる。これは名前渡しと呼ばれる。保留状態の式は継続とも呼ばれる。
  • 数学上の性質から変数は再代入されない。この視点による変数への初期代入は変数への値の束縛と呼ばれる。この仕組みは束縛変数と呼ばれる。式の引数も束縛変数である。再代入禁止は不変性と言い換えられる。
  • 束縛変数に対する自由変数という概念がある。自由変数は(A)未束縛状態の変数、(B)式内で引数以外が束縛されている変数、(C)式内で再代入可能になっている変数など複数の意味がある。(A)はパターンマッチングで大きな意味を持つ。

宣言型プログラミングとは上述の関数の性質および式、遅延評価、名前渡し、継続、束縛変数、不変性、自由変数といった要点の用法に専念しているプログラミングである。別の言い方をすると命令型プログラミングでも上述の関数の性質と要点の用法に専念すれば、それは自ずと宣言型プログラミングになる。パラダイム間でこれらの性質を特に受け継いでいるのが、関数型プログラミング論理プログラミングデータフロープログラミングなどである。他にも制約プログラミングはそれだけで独立しているものではないが、全称量化子と個体の間に横たわるあらゆる中間状態を指した制約という情報要素を扱う性質から、必然的に宣言型における関数の性質と要点の用法に専念されていることが多い。関数は存在量化子と同義であり、制約も関数とは異なる形態の存在量化子なので、後者を扱うための前者は参照透過である方が都合がよいからである。

利点

宣言型プログラミングは以下の利点を持つ[1]

  • 状態の分離: 宣言部分の出力を予測するために状態履歴とそれを起こすコードを探る必要が無くなる

宣言型プログラミングでは宣言部分と状態を分離できる。なぜなら宣言部分ではあるべき状態を宣言するため、その前にどうなっていたかは無関係であるからである。例えば「廊下の電気はONである」と宣言した場合、現在の廊下の電気がONであれOFFであれ、(宣言された)なるべき状態はONであり、現在の状態とは無関係である。すなわち宣言型プログラミングでは時間と共にどう変わるか(=状態)を宣言部分で考える必要がなくなる[2]

もし命令型プログラミングを用いて「廊下のスイッチを押す」という命令をコーディングして廊下の電気を制御した場合、実行後の電気がONかOFFかは現在の値に依存する。ゆえに出力を予測するには状態の履歴を知っている必要がある。そして状態の履歴を知るためには、状態を操作しうる他のコードを把握し、その操作履歴を知る必要がある。ON/OFFの2状態ならまだしも、多数の状態が相互作用する多数のオブジェクトから操作される場合、状態の予測は著しく困難になり、デバッグを含めたプログラミングが難しくなりうる。一方で宣言型プログラミングの場合、宣言部分は状態履歴を必要としないため、宣言部分の出力は常に明確である。

注意点として、状態は分離されているのであり、状態が消滅したわけではない。宣言型プログラミングの場合、light変数にてあるべきライトの状態 "ON"/"OFF" を保持しておき、現在の時刻に基づいてlight変数を切り替え、それが「廊下の電気は{light}である」という宣言に反映されて実際に廊下の電気がONになるというような形になる。light変数という状態は存在しており、これが宣言部分と分離され、宣言部分は最新のlight変数が示す今の電気があるべき状態のみを反映した(過去の電気状態履歴とは無関係な)形になっている[3]。命令的プログラミングで問題となった予測の困難さは、light変数の履歴予測の困難さに分離されている。light変数を誰がいつ操作したかは依然として追跡の難しい問題であるが、宣言部分が分離されたことで問題の所在が限局したものになっている。

脚注

  1. 時間軸と何が起きたかを意識せずに宣言的に記述できる sonatard. (2019) 宣言的UI. p.37
  2. Here is the critical thing. We no longer need to think about how our UI changes over time. What happens is, when we get in the data, we show what it should look like. We show what the next state is. And then framework controls how to get from one state into the other. And so now we no longer need to think about it. And that's the critical piece. Leland Richardson (2019-10-24) "Understanding Compose (Android Dev Summit '19)"
  3. 前回のViewの状態に依存せずに、最終的に描画されるViewを宣言的に記述できる sonatard. (2019) 宣言的UI. p.37

参考文献

関連項目

外部リンク

This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.