「宣言型プログラミング」の版間の差分
m lispと数学と宣言型プログラミング言語についての記述を整理しました |
Shohei KIMURA (会話 | 投稿記録) m編集の要約なし |
||
(14人の利用者による、間の37版が非表示) | |||
1行目: | 1行目: | ||
{{プログラミング・パラダイム}}'''宣言型プログラミング'''({{lang-en-short|Declarative programming}})は、[[数理論理学]]的な性質を表わしている総称的な[[プログラミングパラダイム]]である。[[式 (プログラミング)|式]]の計算構造を、主に[[表示的意味論]]下のロジックで表現する構文にされることが多く、[[式 (プログラミング)|式]]枠外の[[副作用 (プログラム)|副作用]]を伴なう[[制御フロー]]や[[自由変数と束縛変数|自由変数]]の多用などは排除されるようになる<ref>{{citation|last=Lloyd|first=J.W.|title=Practical Advantages of Declarative Programming}}</ref>。計算構造は[[演繹|演繹的]]に組み立てられることが多い。[[命令型プログラミング]]と対をなしての[[プログラミング言語]]の分類用語としても扱われている。{{#tag:ref|ここでは純粋関数を要求しているが、宣言型プログラミングは純粋関数型言語に限定されないことに十分な注意を要す|group="注釈"}}} |
|||
{{プログラミング・パラダイム}} |
|||
宣言型言語は、what the program must accomplish(何をなすべきか)方針で、[[副作用 (プログラム)|副作用]]を排除した式や純粋関数の実装に努める<ref name=":0">"what declarative programming is. Intuitively, it is programming by defining the what (the results we want to achieve) without explaining the how (the algorithms, etc., needed to achieve the results). " P. Van Roy and S. Haridi (2001). ''[[コンピュータプログラミングの概念・技法・モデル]]''. p.117.</ref>。これは命令型言語の、how to accomplish it(どうなすべきか)方針で、副作用を前提にした[[操作的意味論]]下の[[アルゴリズム]]実装とよく対比される<ref name="FOLDOC 2004">{{cite web|title=declarative language|website=FOLDOC|date=17 May 2004|url=https://foldoc.org/declarative%20language|access-date=26 January 2020}}</ref>。 |
|||
{{脚注の不足|date=2021年3月}} |
|||
宣言的パラダイムは、[[関数型プログラミング|関数型]]、[[論理プログラミング|論理型]]、[[データフロープログラミング|データフロー]]などを包括し、[[問い合わせ言語|データベース問い合わせ言語]]、[[マークアップ言語]]、[[ドメイン固有言語]]、[[構成管理]]、[[正規表現]]などにも言及されており、[[並行計算]]との親和性も特筆されている<ref name="Sebesta 2016">{{cite book|last=Sebesta|first=Robert|title=Concepts of programming languages|publisher=Pearson|publication-place=Boston|year=2016|isbn=978-0-13-394302-3|oclc=896687896}}</ref>。 |
|||
{{複数の問題 |
|||
| section = 1 |
|||
| 出典の明記 = 2021年3月3日 (水) 12:25 (UTC) |
|||
| 独自研究 = 2021年3月3日 (水) 12:25 (UTC) |
|||
}} |
|||
== 概要 == |
|||
'''宣言型プログラミング'''({{lang-en-short|''Declarative programming''}})は、総称的に用いられる[[プログラミングパラダイム]]であり、[[プログラミング言語]]および[[プログラミングパラダイム|パラダイム間]]の分類用語として使われることが多い。対義語である[[命令型プログラミング]]との対比概念としてよく用いられる。宣言型は[[プログラミング|プログラミング理論]]のバックボーンである[[数学]]および[[形式論理学]]の性質に準拠したものであり、[[オートマトン|オートマタ理論]]的には[[チューリングマシン|決定性チューリングマシン]]に沿っている。前提と結論、定義と推論、問題と解答、条件と結果、入力と出力などが時系列に関係なく正確に対応して例外なく明示されており、その触媒となる作用、写像、演算などは原則として[[冪等]]であり、それに対する入力要素と出力要素が不可欠になっているという性質を指して宣言的(''declarative'')または平叙的(''declarative'')と言われる。 |
|||
宣言型プログラミングは、現行式枠外の外部状態への代入[[コマンド (コンピュータ)|コマンド]]、および外部状態の現行式への影響([[副作用 (プログラム)|副作用]])といった命令的な性質を持たないパラダイムとして定義されている。命令的性質の[[文 (プログラミング)|ステートメント]]に対して、宣言的なプログラムの基本は[[式 (プログラミング)|式]]とされる。 |
|||
コマンドと副作用を持つ命令的なオペレータ([[手続き]]・[[関数 (プログラミング)|関数]]・[[サブルーチン]])は、計算内容のリスト化とステップ単位解釈が必要になるので、これがhow to accomplish it(どうなすべきか)とされる。 |
|||
概略すると「一つの[[トランザクション]]または一つの[[プログラム (コンピュータ)|プログラム]]実行枠内でのあらゆるオペレーションにおいて、その遷移作用原因になる全情報要素と全環境状態をオペレーションのインプットに内包し、その遷移作用結果になった全情報要素と全環境状態をオペレーションのアウトプットに内包して、遷移作用の法則が不変になっている」というプロセス全般または作業全般を指して宣言的と言われる。遷移作用(''transition function'')の[[冪等|冪等性]]によるインプットとアウトプットの対偶関係は[[参照透過性]]を表わす。サブルーチン処理内容に重点を置いている命令型に対して、宣言型は引数と返り値に焦点を当てているとも言える。 |
|||
コマンドと副作用を持たない宣言的なオペレータは、その定義だけで計算内容を把握できるので、これがwhat to accomplish it(何をなすべきか)とされる。命令的オペレータを用いずに、宣言的オペレータを用いることが即ち宣言的なプログラムになる<ref name=":0" /><ref>「宣言的記述を行う高水準言語の主要なお題目は『どうやって計算するか(How)ではなく, 何を計算するか(What)を記述する』というものである.」 ({{Cite journal|和書|author=近山隆 |year=2014 |url=https://doi.org/10.11309/jssst.31.2_8 |title=ソフトウェアの30年とこれから |journal=コンピュータ ソフトウェア |ISSN=0289-6540 |publisher=日本ソフトウェア科学会 |volume=31 |issue=2 |page=9 |doi=10.11309/jssst.31.2_8 |CRID=1390282679715495936}})</ref>。式は[[演算子 (コンピュータ言語)|オペレータ]]、[[被演算子|オペランド]]、他の式、[[再帰]]式などの組み合わせになる。 |
|||
== 来歴 == |
|||
宣言的パラダイムにあるべき特徴は以下のようになる。 |
|||
{{複数の問題 |
|||
| section = 1 |
|||
| 出典の明記 = 2021年3月3日 (水) 12:25 (UTC) |
|||
| 独自研究 = 2021年3月3日 (水) 12:25 (UTC) |
|||
}} |
|||
# 計算を主に[[表示的意味論]]で記述する[[高水準言語]] |
|||
宣言型プログラミングは、プログラミング言語のための分類用語としての性格が強い総称的なプログラミングパラダイムなので、どうしてそのような分類が必要になったのかという視点から解釈するのが理解への早道になる。宣言型と命令型という二大分類が定義された背景には、数学や[[形式論理学]]([[数理論理学]])をバックボーンにしたコンピュータ原理とプログラミング言語理論の成立過程がある。あらゆる問題への解法において、数学や[[形式論理学]]の性質を引き継いでいる従来の方式が宣言型であり、[[計算機科学]]が持つようになった特有の方式が命令型である。宣言型は問題と解答の二者限定の平叙な関係の計算であるのに対して、その関係に状態(''state'')というものが加えられた計算の方は命令型となった。”状態”は問題内で宣言されていない潜在要素であり、命令型の計算は同じ問題でもその時の”状態”によって解答が変化した。計算過程による”状態”の変動とそれによる後続計算への影響は[[副作用 (プログラム)|副作用]]と呼ばれる。”状態”の影響が無い平叙な計算と、”状態”に影響される手続き的な計算はコンピュータ計算性質の根本的な分かれ目になったので、前者の宣言型と後者の命令型というプログラミング理論上の二大分類が生まれた。 |
|||
# [[参照透過性|参照透過]]を担保できるように表現された副作用 |
|||
# 計算そのものを計算対象にできるという[[高階論理|高階]]なプログラム |
|||
#[[数理論理学]]に準拠したプログラム<ref>{{cite thesis|first=Manuel M. T.|last=Chakravarty|date=14 February 1997|url=http://www.cse.unsw.edu.au/~chak/papers/diss.ps.gz|title=On the Massively Parallel Execution of Declarative Programs|type=Doctoral dissertation|publisher=[[ベルリン工科大学|Technical University of Berlin]]|accessdate=26 February 2015|quote=In this context, the criterion for calling a programming language declarative is the existence of a clear, mathematically established correspondence between the language and mathematical logic such that a declarative semantics for the language can be based on the model or the proof theory (or both) of the logic.}}</ref> |
|||
宣言的オペレータの性質である[[参照透過性]]は、同じ引数に対するオペレータの動作と返り値が不変であることを意味する<ref>"We say the operation is declarative if, whenever called with the same arguments, it returns the same results independent of any other computation state." P. Van Roy and S. Haridi (2001). ''[[コンピュータプログラミングの概念・技法・モデル]]''. p.113.</ref>。 |
|||
[[オートマトン|オートマタ理論]]的には、計算は遷移関数(''transition function'')、問題は入力値、解答は出力値、状態は遷移関数の[[非決定性チューリングマシン|非決定性]]に読み替えられる。プログラム的には、計算はサブルーチン、問題は引数、解答は返り値、状態はサブルーチン内外の静的メモリデータに読み替えられる。状態による計算の複雑性の拡充は、[[ノイマン型]]のコンピュータ機構に適したものだったので[[機械語]]と[[低水準言語]]([[アセンブリ言語|アセンブラ]])はその必要性から命令型で構築された。また最初期の[[高水準言語]]([[FORTRAN]]、[[COBOL]]、[[ALGOL]])も[[ノイマン型]]の性質に適した命令型になった。1950年代半ばからの高水準言語の普及につれて、命令型が利点的に拡充した計算の複雑性が今度は問題として挙げられるようになり、1960年代になると数学への原点回帰とも言える宣言型がその解決策として注目され始めた。また[[並行計算]]と宣言型の適性も認識され始めた。1970年代になると宣言型は非ノイマン型コンピュータ構築のためのパラダイムとしても重視されるようになった。 |
|||
式は単体で評価されるほか、引数に適用されて評価値(返り値)になる。式はほかへの引数にもなり、[[高階論理]]の式は他の式を引数にする。[[微分]]の[[導関数]]と同様に、式の返り値を式にすることもできる。引数や返り値にもできる式は、[[第一級オブジェクト]]とされる。 |
|||
1960年代から[[メインフレーム]]上の[[並行計算]][[データフロープログラミング|データフロープログラム]]が普及しそのための[[低水準言語]]はより平叙的な性質を示していた。また[[述語論理]]を基礎にした[[論理プログラミング]]の研究も始まりその中で実装された数々の言語もより平叙的な性質を持っていたが、1969年稼働の「[[Planner]]」は手続き的性質を備えていた。 |
|||
SQLのクエリは「どのようなデータが欲しいか」を宣言し、「いかにしてデータベースにアクセスするか」という命令・手続きには関与しない。[[関数型言語]]の多くは、コマンドと副作用の取り扱いも許容している命令型と宣言型の折衷になっている。純粋関数型言語は宣言的に徹しており、プログラム[[正当性 (計算機科学)|正当性]]の[[形式的検証]]を可能にしている{{#tag:ref|副作用を完全に排除してしまうと、大抵の場合はコンピュータ言語として機能しなくなる(画面への表示やファイルなどへの読み書きも副作用とされているので、実行結果を得ることすらできない)ので、参照透過性だけを保証して副作用を許容している。|group="注釈"}}。加えて副作用は常に失敗する可能性と隣り合わせだが、宣言型プログラミングでは副作用の使用を制限し、純粋なコードから分離することで、失敗への対処漏れがあったとしても問題点を突き止めやすくなるというメリットもある。 |
|||
== 特徴 == |
|||
宣言型の最大の特徴である形式的検証に対して、命令型では[[クラス (コンピュータ)|クラス]]や[[オブジェクト (プログラミング)|オブジェクト]]を宣言的フレームワークに内包して局面的な宣言的オペレータにしてその動作を検証することがある。[[Java]]テストフレームワーク[[JUnit]]などが例である。 |
|||
宣言型は[[並行プログラミング]]との親和性が高いことも特筆される。これは[[セマフォ]]や[[ミューテックス]]や読み書き[[ロック (計算機科学)|ロック]]などを駆使して[[同期 (計算機科学)|同期]]的な[[並行性]]を実現することが多い[[命令型プログラミング|命令型]]に対するアドバンテージである。宣言型は、式としてのプロセスを[[プロセス代数|代数]]として扱えるので、その代数を他のプロセスへの引数としての[[メッセージ (コンピュータ)|メッセージ]]にしつつ、部分計算や[[評価戦略]]を応用しての非同期な並行性を実現できる。 |
|||
{{複数の問題 |
|||
| section = 1 |
|||
| 出典の明記 = 2021年3月3日 (水) 12:25 (UTC) |
|||
| 独自研究 = 2021年3月3日 (水) 12:25 (UTC) |
|||
}} |
|||
== 利点 == |
|||
宣言型プログラミングとは端的に述べると、あらゆる遷移関数(''transition function'')において、状態遷移の原因になるあらゆる情報要素とあらゆる環境情報を遷移関数の引数に収納しており、状態遷移の結果になったあらゆる情報要素とあらゆる環境情報が遷移関数の返り値に収納されており、それら引数と返り値と遷移関数の整合性を維持するために、同じ引数に対する遷移関数の処理内容と返り値の一定化が保証されているというプロセス全般の構築を意味している。これらの仕様は主に[[参照透過性]]というプログラム概念に結び付けられている。これらの仕様を実現するための実装スタイルを箇条書きすると大抵は以下のようなものになる。 |
|||
=== 計算式の抽象値化 === |
|||
[[参照透過性|参照透過]]な計算式は、時と場所に左右されない抽象値にもなる<ref>時間軸と何が起きたかを意識せずに宣言的に記述できる [https://speakerdeck.com/sonatard/xuan-yan-de-ui?slide=37 sonatard. (2019) 宣言的UI. p.37]</ref>。いつどこで計算されても同じ引数に対して同じ結果が返るならば、あえて計算しないままにしておいての抽象値として扱おう<ref>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. |
|||
[https://www.youtube.com/watch?v=Q9MtlmmN4Q0 Leland Richardson (2019-10-24) "Understanding Compose (Android Dev Summit '19)"]</ref>とするのが宣言型のアプローチである。この概念の実装例は[[クロージャ]]である。クロージャは、他の式や関数の束縛変数にされることが前提のレキシカルスコープ基準の[[無名関数]]である。 |
|||
* '''関数'''へのパラメータ引数に状態遷移の原因になるあらゆる情報要素と環境要素が内包されており、同関数からのリターン値に状態遷移の結果になったあらゆる情報要素と環境要素が内包されている。 |
|||
* 同じパラメータ引数に対する関数の処理内容は常に同一であり、そのリターン値も常に同一である。 |
|||
* 関数の実行には原則的にパラメータ引数の入力を必要とする。入力の無い関数は上述の状態と副作用に依存した稼働方式になるからである。 |
|||
* 実行された関数は原則的にリターン値の出力を要求される。出力の空白は次の関数への入力の喪失を意味するのでプロセス停止に繋がる。 |
|||
*'''式'''をプログラムの基本単位とする。式は関数または演算子をノードにした入力と出力の連鎖体であり、式自体が最終出力値と同義になる。式は初期入力値を与えられて計算実行される。この計算実行は評価と呼ばれる。初期入力値は引数と呼ばれる。最終出力値は評価値と呼ばれる。 |
|||
*初期入力値を与えられた式はその計算実行を保留する事もできる。その保留状態の式の任意時の計算実行は'''遅延評価'''と呼ばれる。 |
|||
*保留状態の式を値として他の式の初期入力値にする事もできる。これは'''名前渡し'''と呼ばれる。保留状態の式は'''継続'''とも呼ばれる。 |
|||
*数学上の性質から変数は再代入されない。この視点による変数への初期代入は変数への値の束縛と呼ばれる。この仕組みは'''束縛変数'''と呼ばれる。式の引数も束縛変数である。再代入禁止は'''不変性'''と言い換えられる。 |
|||
*束縛変数に対する'''自由変数'''という概念がある。自由変数は(A)未束縛状態の変数、(B)式内で引数以外が束縛されている変数、(C)式内で再代入可能になっている変数など複数の意味がある。(A)はパターンマッチングで大きな意味を持つ。 |
|||
宣言型プログラミングとは上述の関数の性質および式、遅延評価、名前渡し、継続、束縛変数、不変性、自由変数といった要点の用法に専念しているプログラミングである。別の言い方をすると命令型プログラミングでも上述の関数の性質と要点の用法に専念すれば、それは自ずと宣言型プログラミングになる。パラダイム間でこれらの性質を特に受け継いでいるのが、[[関数型プログラミング]]、[[論理プログラミング]]、[[データフロープログラミング]]などである。他にも[[制約プログラミング]]はそれだけで独立しているものではないが、[[全称量化子]]と個体の間に横たわるあらゆる中間状態を指した制約という情報要素を扱う性質から、必然的に宣言型における関数の性質と要点の用法に専念されていることが多い。関数は[[存在量化子]]と同義であり、制約も関数とは異なる形態の[[存在量化子]]なので、後者を扱うための前者は[[参照透過性|参照透過]]である方が都合がよいからである。 |
|||
もう一つの実装例は、宣言的な[[関数オブジェクト]]である。そこでは処理+返り値の青写真になる不変部分と、引数で決定される処理+返り値の可変部分が明確に分離されている。宣言型は、[[段階的詳細化法|段階的詳細化]]した各要素を不変部分と可変部分の構成体にすることをアプローチする。これに準拠した技術の仮想[[Document Object Model|DOM]]は、[[XML]]や[[HyperText Markup Language|HTML]]文書を変換した[[ツリー構造]]の各ノードを、不変+可変構成の宣言的オブジェクトに再変換している<ref>"programming concept where an ideal ... representation ... is kept in memory and synced with the “real” DOM by a '''library''' ... This approach enables the declarative API ... : You tell '''React''' what state you want the UI to be in, and '''it''' makes sure the DOM matches that state. This abstracts out the attribute manipulation, event handling ..." React. ''[https://reactjs.org/docs/faq-internals.html Virtual DOM and Internals]''.</ref>。その不変部分は不変状態と同義になり、しばしば[[イミュータブル]]の概念で説明される。 |
|||
== 利点 == |
|||
宣言型プログラミングは以下の利点を持つ<ref>時間軸と何が起きたかを意識せずに宣言的に記述できる [https://speakerdeck.com/sonatard/xuan-yan-de-ui?slide=37 sonatard. (2019) 宣言的UI. p.37]</ref>。 |
|||
=== 計算の最適化 === |
|||
* 状態の分離: 宣言部分の出力を予測するために状態履歴とそれを起こすコードを探る必要が無くなる |
|||
[[グラフィカルユーザインタフェース#宣言的UI|宣言的UI]]は、[[命令型プログラミング|命令的]]な[[オブジェクト指向]]UI(OOUI)と対比されて一長一短があるが、軽量さと再描画速度での利点が挙げられやすい。徹底的な遅延結合を旨とするOOUIでは、何かの更新イベントが発生する度に、UI要素間のメソッドの呼び合い([[メッセージ (コンピュータ)|メッセージ]])とUI要素それぞれの総合的な再解釈が行われがちである。宣言的UIでは、各UI要素は青写真としての不変部分を持ち、可変部分に適用される引数はメモ化されていて、同じ引数が渡された場合は計算スキップされる<ref>"declarative UI ... works by conceptually regenerating the entire screen from scratch, then applying only the necessary changes." ''[https://developer.android.com/jetpack/compose/mental-model#paradigm Thinking in Compose]''. Jetpack Compose.</ref>。更新イベントは各UI要素への引数に変換されて、差異引数を渡されたUI要素だけが再解釈されるので再描画計算量は軽減されやすい<ref>"React provides a declarative API so that you don’t have to worry about exactly what changes on every update." React. ''[https://reactjs.org/docs/reconciliation.html Reconciliation]''.</ref>。そこでは以前の描画状態を鑑みる必要はない<ref>前回のViewの状態に依存せずに、最終的に描画されるViewを宣言的に記述できる [https://speakerdeck.com/sonatard/xuan-yan-de-ui?slide=37 sonatard. (2019) 宣言的UI. p.37]</ref>。このようにしなくていい計算を明確化して全体の最適化に繋げるのが宣言型のアプローチである。 |
|||
また、宣言的構造は不変部分と可変部分が明確に区分けされるので、何もかもが遅延結合のミュータブルになりがちなオブジェクト/メッセージ構造よりも平易かつ明瞭になるという利点もある。 |
|||
宣言型プログラミングでは'''宣言部分と状態を分離'''できる。なぜなら宣言部分ではあるべき状態を宣言するため、その前にどうなっていたかは無関係であるからである。例えば「廊下の電気はONである」と宣言した場合、現在の廊下の電気がONであれOFFであれ、(宣言された)なるべき状態はONであり、現在の状態とは無関係である。すなわち宣言型プログラミングでは時間と共にどう変わるか(=状態)を宣言部分で考える必要がなくなる<ref>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. |
|||
=== 未来要素を内包した計算 === |
|||
[https://www.youtube.com/watch?v=Q9MtlmmN4Q0 Leland Richardson (2019-10-24) "Understanding Compose (Android Dev Summit '19)"]</ref>。 |
|||
[[参照透過性|参照透過]]な計算式は、次以降の式で確定される未来要素([[前方参照]]や[[遅延評価]])を残したままの結果値を返すこともできる。その結果値とは、次以降の式で引数が与えられることを前提にした[[第一級関数]]になる。それは次以降の式で確定される前方参照要素を残したままの未評価式であり、次の式の[[束縛変数]]にされるか、式枠外の変数に束縛されて保存されるなどして、次以降の式から渡される引数によって最終的な結果値になる。 |
|||
宣言型は、次以降の式で確定される前方参照要素を残したままの抽象値の取り扱いをアプローチする。その実装例には[[Future (プログラミング)|Future]]などがある。それらを高度に応用した数学理論が[[並行プログラミング]]分野の[[アクターモデル]]や[[プロセス代数]]である。 |
|||
また宣言型は、前方参照要素を残した抽象値同士をそのまま計算することもアプローチする。そこでは[[部分評価]]や部分計算などの数学理論が用いられる。[[制約プログラミング]]はそれらに準拠している。 |
|||
=== 副作用を排した純粋な計算 === |
|||
[[参照透過性|参照透過]]な計算式では、式枠外の状態(データなど)は完全に無視される。計算式が外部状態を扱えるのは、それが引数として渡された時のみである。その引数を加工した返り値は、そのままではただのデータであり、それが外部状態に反映されるかは式枠外のプロセスの担当になる。また、参照透過な計算式は、式枠内にも可変の状態(データなど)を持つことはできない。可変の内部状態に依存した計算ではその[[冪等]]性が失われるからである。これらの性質は純粋関数とも呼ばれる。 |
|||
例として、押すたびにオン/オフが切り替わる電気スイッチ・オペレータがあるとする。命令的オペレータでは現状のオン/オフを状態保存できるので、別途変数を参照するという形式で、オペレータとオン/オフ状態をユニット実装できる。これに対して宣言的オペレータでは、引数として渡されたオン/オフ状態を参照するという形式になり、そこではオペレータとオン/オフ状態を別々に扱うことになる。これだけだと宣言的の方が、ただ煩雑に見える。しかしその”状態”に、オン/オフだけでなく昼夜の区別やアンペア許容やその他諸々の要素が加わっていくと、そうではなくなるというのが宣言型のアプローチである。 |
|||
その時に必要な対象値と”状態”をセットにしての純粋関数を実現する手法としては、[[部分構造論理]]由来のサブ構造型システムと、[[圏論]]由来の[[モナド (プログラミング)|モナド]]などがある。 |
|||
=== 状態の分離 === |
|||
宣言型プログラミングでは'''宣言部分と状態を分離'''できる。なぜなら宣言部分ではあるべき状態を宣言するため、その前にどうなっていたかは無関係であるからである。例えば「廊下の電気はONである」と宣言した場合、現在の廊下の電気がONであれOFFであれ、(宣言された)なるべき状態はONであり、現在の状態とは無関係である。すなわち宣言型プログラミングでは時間と共にどう変わるか(=状態)を宣言部分で考える必要がなくなる<ref>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. [https://www.youtube.com/watch?v=Q9MtlmmN4Q0 Leland Richardson (2019-10-24) "Understanding Compose (Android Dev Summit '19)"]</ref>。 |
|||
もし命令型プログラミングを用いて「廊下のスイッチを押す」という命令をコーディングして廊下の電気を制御した場合、実行後の電気がONかOFFかは現在の値に依存する。ゆえに出力を予測するには状態の履歴を知っている必要がある。そして状態の履歴を知るためには、状態を操作しうる他のコードを把握し、その操作履歴を知る必要がある。ON/OFFの2状態ならまだしも、多数の状態が相互作用する多数のオブジェクトから操作される場合、状態の予測は著しく困難になり、デバッグを含めたプログラミングが難しくなりうる。一方で宣言型プログラミングの場合、宣言部分は状態履歴を必要としないため、宣言部分の出力は常に明確である。 |
もし命令型プログラミングを用いて「廊下のスイッチを押す」という命令をコーディングして廊下の電気を制御した場合、実行後の電気がONかOFFかは現在の値に依存する。ゆえに出力を予測するには状態の履歴を知っている必要がある。そして状態の履歴を知るためには、状態を操作しうる他のコードを把握し、その操作履歴を知る必要がある。ON/OFFの2状態ならまだしも、多数の状態が相互作用する多数のオブジェクトから操作される場合、状態の予測は著しく困難になり、デバッグを含めたプログラミングが難しくなりうる。一方で宣言型プログラミングの場合、宣言部分は状態履歴を必要としないため、宣言部分の出力は常に明確である。 |
||
注意点として、状態は分離されているのであり、状態が消滅したわけではない。宣言型プログラミングの場合、light変数にてあるべきライトの状態 "ON"/"OFF" を保持しておき、現在の時刻に基づいてlight変数を切り替え、それが「廊下の電気は{light}である」という宣言に反映されて実際に廊下の電気がONになるというような形になる。light変数という状態は存在しており、これが宣言部分と分離され、宣言部分は最新のlight変数が示す今の電気があるべき状態のみを反映した(過去の電気状態履歴とは無関係な)形になっている |
注意点として、状態は分離されているのであり、状態が消滅したわけではない。宣言型プログラミングの場合、light変数にてあるべきライトの状態 "ON"/"OFF" を保持しておき、現在の時刻に基づいてlight変数を切り替え、それが「廊下の電気は{light}である」という宣言に反映されて実際に廊下の電気がONになるというような形になる。light変数という状態は存在しており、これが宣言部分と分離され、宣言部分は最新のlight変数が示す今の電気があるべき状態のみを反映した(過去の電気状態履歴とは無関係な)形になっている。命令的プログラミングで問題となった予測の困難さは、light変数の履歴予測の困難さに分離されている。light変数を誰がいつ操作したかは依然として追跡の難しい問題であるが、宣言部分が分離されたことで問題の所在が限局したものになっている。 |
||
==宣言型言語の例== |
|||
宣言型に準拠したプログラミング言語 |
|||
*関数型 - [[Erlang]]、[[Haskell]]、[[LISP]] |
|||
*論理型 - [[Prolog]]、[[Concurrent Prolog]]、[[Guarded Horn Clauses|GHC]]、[[:en:Mercury programming language|Mercury]] |
|||
*制約 - [[Oz (プログラミング言語)|Oz]]、[[Constraint Handling Rules]] |
|||
宣言型に準拠したドメイン固有言語 |
|||
*[[XSL Transformations|XSLT]] は、[[Extensible Markup Language|XML]]文書の変換のためのチューリング完全な宣言型言語 |
|||
*[[SQL]] は、[[関係データベース]]のクエリのための宣言的要素を持つ。チューリング完全。 |
|||
*[[Nix Expression Language]] |
|||
*[[Dhall]] |
|||
宣言型を適用したフレームワーク各種 |
|||
*[[JUnit]] |
|||
*{{節リンク|グラフィカルユーザインタフェース|宣言的UI}} |
|||
== 脚注 == |
== 脚注 == |
||
{{脚注ヘルプ}} |
{{脚注ヘルプ}} |
||
=== 注釈 === |
|||
{{notelist}} |
|||
=== 出典 === |
|||
{{Reflist}} |
{{Reflist}} |
||
73行目: | 93行目: | ||
== 関連項目 == |
== 関連項目 == |
||
*[[数理論理学]] |
|||
*[[参照透過性]] |
|||
*[[表示的意味論]] |
|||
*[[ドメイン固有言語]] |
*[[ドメイン固有言語]] |
||
*[[問い合わせ言語]] |
*[[問い合わせ言語]] |
||
*[[マークアップ言語]] |
*[[マークアップ言語]] |
||
*[[モデリング言語]] |
|||
== 外部リンク == |
== 外部リンク == |
||
* Frans Coenen. [http://www.csc.liv.ac.uk/~frans/OldLectures/2CS24/declarative.html#detail Characteristics of declarative programming languages]. 1999. |
* Frans Coenen. [http://www.csc.liv.ac.uk/~frans/OldLectures/2CS24/declarative.html#detail Characteristics of declarative programming languages]. 1999. |
||
89行目: | 112行目: | ||
{{Normdaten}} |
{{Normdaten}} |
||
{{DEFAULTSORT:せんけんかたふろくらみんく}} |
{{DEFAULTSORT:せんけんかたふろくらみんく}} |
||
[[Category:プログラミングパラダイム]] |
[[Category:プログラミングパラダイム]] |
2024年7月28日 (日) 02:45時点における最新版
プログラミング・パラダイム |
---|
宣言型プログラミング(英: Declarative programming)は、数理論理学的な性質を表わしている総称的なプログラミングパラダイムである。式の計算構造を、主に表示的意味論下のロジックで表現する構文にされることが多く、式枠外の副作用を伴なう制御フローや自由変数の多用などは排除されるようになる[1]。計算構造は演繹的に組み立てられることが多い。命令型プログラミングと対をなしてのプログラミング言語の分類用語としても扱われている。[注釈 1]}
宣言型言語は、what the program must accomplish(何をなすべきか)方針で、副作用を排除した式や純粋関数の実装に努める[2]。これは命令型言語の、how to accomplish it(どうなすべきか)方針で、副作用を前提にした操作的意味論下のアルゴリズム実装とよく対比される[3]。
宣言的パラダイムは、関数型、論理型、データフローなどを包括し、データベース問い合わせ言語、マークアップ言語、ドメイン固有言語、構成管理、正規表現などにも言及されており、並行計算との親和性も特筆されている[4]。
概要
[編集]宣言型プログラミングは、現行式枠外の外部状態への代入コマンド、および外部状態の現行式への影響(副作用)といった命令的な性質を持たないパラダイムとして定義されている。命令的性質のステートメントに対して、宣言的なプログラムの基本は式とされる。
コマンドと副作用を持つ命令的なオペレータ(手続き・関数・サブルーチン)は、計算内容のリスト化とステップ単位解釈が必要になるので、これがhow to accomplish it(どうなすべきか)とされる。
コマンドと副作用を持たない宣言的なオペレータは、その定義だけで計算内容を把握できるので、これがwhat to accomplish it(何をなすべきか)とされる。命令的オペレータを用いずに、宣言的オペレータを用いることが即ち宣言的なプログラムになる[2][5]。式はオペレータ、オペランド、他の式、再帰式などの組み合わせになる。
宣言的パラダイムにあるべき特徴は以下のようになる。
宣言的オペレータの性質である参照透過性は、同じ引数に対するオペレータの動作と返り値が不変であることを意味する[7]。
式は単体で評価されるほか、引数に適用されて評価値(返り値)になる。式はほかへの引数にもなり、高階論理の式は他の式を引数にする。微分の導関数と同様に、式の返り値を式にすることもできる。引数や返り値にもできる式は、第一級オブジェクトとされる。
SQLのクエリは「どのようなデータが欲しいか」を宣言し、「いかにしてデータベースにアクセスするか」という命令・手続きには関与しない。関数型言語の多くは、コマンドと副作用の取り扱いも許容している命令型と宣言型の折衷になっている。純粋関数型言語は宣言的に徹しており、プログラム正当性の形式的検証を可能にしている[注釈 2]。加えて副作用は常に失敗する可能性と隣り合わせだが、宣言型プログラミングでは副作用の使用を制限し、純粋なコードから分離することで、失敗への対処漏れがあったとしても問題点を突き止めやすくなるというメリットもある。 宣言型の最大の特徴である形式的検証に対して、命令型ではクラスやオブジェクトを宣言的フレームワークに内包して局面的な宣言的オペレータにしてその動作を検証することがある。JavaテストフレームワークJUnitなどが例である。
宣言型は並行プログラミングとの親和性が高いことも特筆される。これはセマフォやミューテックスや読み書きロックなどを駆使して同期的な並行性を実現することが多い命令型に対するアドバンテージである。宣言型は、式としてのプロセスを代数として扱えるので、その代数を他のプロセスへの引数としてのメッセージにしつつ、部分計算や評価戦略を応用しての非同期な並行性を実現できる。
利点
[編集]計算式の抽象値化
[編集]参照透過な計算式は、時と場所に左右されない抽象値にもなる[8]。いつどこで計算されても同じ引数に対して同じ結果が返るならば、あえて計算しないままにしておいての抽象値として扱おう[9]とするのが宣言型のアプローチである。この概念の実装例はクロージャである。クロージャは、他の式や関数の束縛変数にされることが前提のレキシカルスコープ基準の無名関数である。
もう一つの実装例は、宣言的な関数オブジェクトである。そこでは処理+返り値の青写真になる不変部分と、引数で決定される処理+返り値の可変部分が明確に分離されている。宣言型は、段階的詳細化した各要素を不変部分と可変部分の構成体にすることをアプローチする。これに準拠した技術の仮想DOMは、XMLやHTML文書を変換したツリー構造の各ノードを、不変+可変構成の宣言的オブジェクトに再変換している[10]。その不変部分は不変状態と同義になり、しばしばイミュータブルの概念で説明される。
計算の最適化
[編集]宣言的UIは、命令的なオブジェクト指向UI(OOUI)と対比されて一長一短があるが、軽量さと再描画速度での利点が挙げられやすい。徹底的な遅延結合を旨とするOOUIでは、何かの更新イベントが発生する度に、UI要素間のメソッドの呼び合い(メッセージ)とUI要素それぞれの総合的な再解釈が行われがちである。宣言的UIでは、各UI要素は青写真としての不変部分を持ち、可変部分に適用される引数はメモ化されていて、同じ引数が渡された場合は計算スキップされる[11]。更新イベントは各UI要素への引数に変換されて、差異引数を渡されたUI要素だけが再解釈されるので再描画計算量は軽減されやすい[12]。そこでは以前の描画状態を鑑みる必要はない[13]。このようにしなくていい計算を明確化して全体の最適化に繋げるのが宣言型のアプローチである。
また、宣言的構造は不変部分と可変部分が明確に区分けされるので、何もかもが遅延結合のミュータブルになりがちなオブジェクト/メッセージ構造よりも平易かつ明瞭になるという利点もある。
未来要素を内包した計算
[編集]参照透過な計算式は、次以降の式で確定される未来要素(前方参照や遅延評価)を残したままの結果値を返すこともできる。その結果値とは、次以降の式で引数が与えられることを前提にした第一級関数になる。それは次以降の式で確定される前方参照要素を残したままの未評価式であり、次の式の束縛変数にされるか、式枠外の変数に束縛されて保存されるなどして、次以降の式から渡される引数によって最終的な結果値になる。
宣言型は、次以降の式で確定される前方参照要素を残したままの抽象値の取り扱いをアプローチする。その実装例にはFutureなどがある。それらを高度に応用した数学理論が並行プログラミング分野のアクターモデルやプロセス代数である。
また宣言型は、前方参照要素を残した抽象値同士をそのまま計算することもアプローチする。そこでは部分評価や部分計算などの数学理論が用いられる。制約プログラミングはそれらに準拠している。
副作用を排した純粋な計算
[編集]参照透過な計算式では、式枠外の状態(データなど)は完全に無視される。計算式が外部状態を扱えるのは、それが引数として渡された時のみである。その引数を加工した返り値は、そのままではただのデータであり、それが外部状態に反映されるかは式枠外のプロセスの担当になる。また、参照透過な計算式は、式枠内にも可変の状態(データなど)を持つことはできない。可変の内部状態に依存した計算ではその冪等性が失われるからである。これらの性質は純粋関数とも呼ばれる。
例として、押すたびにオン/オフが切り替わる電気スイッチ・オペレータがあるとする。命令的オペレータでは現状のオン/オフを状態保存できるので、別途変数を参照するという形式で、オペレータとオン/オフ状態をユニット実装できる。これに対して宣言的オペレータでは、引数として渡されたオン/オフ状態を参照するという形式になり、そこではオペレータとオン/オフ状態を別々に扱うことになる。これだけだと宣言的の方が、ただ煩雑に見える。しかしその”状態”に、オン/オフだけでなく昼夜の区別やアンペア許容やその他諸々の要素が加わっていくと、そうではなくなるというのが宣言型のアプローチである。
その時に必要な対象値と”状態”をセットにしての純粋関数を実現する手法としては、部分構造論理由来のサブ構造型システムと、圏論由来のモナドなどがある。
状態の分離
[編集]宣言型プログラミングでは宣言部分と状態を分離できる。なぜなら宣言部分ではあるべき状態を宣言するため、その前にどうなっていたかは無関係であるからである。例えば「廊下の電気はONである」と宣言した場合、現在の廊下の電気がONであれOFFであれ、(宣言された)なるべき状態はONであり、現在の状態とは無関係である。すなわち宣言型プログラミングでは時間と共にどう変わるか(=状態)を宣言部分で考える必要がなくなる[14]。
もし命令型プログラミングを用いて「廊下のスイッチを押す」という命令をコーディングして廊下の電気を制御した場合、実行後の電気がONかOFFかは現在の値に依存する。ゆえに出力を予測するには状態の履歴を知っている必要がある。そして状態の履歴を知るためには、状態を操作しうる他のコードを把握し、その操作履歴を知る必要がある。ON/OFFの2状態ならまだしも、多数の状態が相互作用する多数のオブジェクトから操作される場合、状態の予測は著しく困難になり、デバッグを含めたプログラミングが難しくなりうる。一方で宣言型プログラミングの場合、宣言部分は状態履歴を必要としないため、宣言部分の出力は常に明確である。
注意点として、状態は分離されているのであり、状態が消滅したわけではない。宣言型プログラミングの場合、light変数にてあるべきライトの状態 "ON"/"OFF" を保持しておき、現在の時刻に基づいてlight変数を切り替え、それが「廊下の電気は{light}である」という宣言に反映されて実際に廊下の電気がONになるというような形になる。light変数という状態は存在しており、これが宣言部分と分離され、宣言部分は最新のlight変数が示す今の電気があるべき状態のみを反映した(過去の電気状態履歴とは無関係な)形になっている。命令的プログラミングで問題となった予測の困難さは、light変数の履歴予測の困難さに分離されている。light変数を誰がいつ操作したかは依然として追跡の難しい問題であるが、宣言部分が分離されたことで問題の所在が限局したものになっている。
宣言型言語の例
[編集]宣言型に準拠したプログラミング言語
- 関数型 - Erlang、Haskell、LISP
- 論理型 - Prolog、Concurrent Prolog、GHC、Mercury
- 制約 - Oz、Constraint Handling Rules
宣言型に準拠したドメイン固有言語
- XSLT は、XML文書の変換のためのチューリング完全な宣言型言語
- SQL は、関係データベースのクエリのための宣言的要素を持つ。チューリング完全。
- Nix Expression Language
- Dhall
宣言型を適用したフレームワーク各種
脚注
[編集]注釈
[編集]出典
[編集]- ^ Lloyd, J.W., Practical Advantages of Declarative Programming
- ^ a b "what declarative programming is. Intuitively, it is programming by defining the what (the results we want to achieve) without explaining the how (the algorithms, etc., needed to achieve the results). " P. Van Roy and S. Haridi (2001). コンピュータプログラミングの概念・技法・モデル. p.117.
- ^ “declarative language”. FOLDOC (17 May 2004). 26 January 2020閲覧。
- ^ Sebesta, Robert (2016). Concepts of programming languages. Boston: Pearson. ISBN 978-0-13-394302-3. OCLC 896687896
- ^ 「宣言的記述を行う高水準言語の主要なお題目は『どうやって計算するか(How)ではなく, 何を計算するか(What)を記述する』というものである.」 (近山隆「ソフトウェアの30年とこれから」『コンピュータ ソフトウェア』第31巻第2号、日本ソフトウェア科学会、2014年、9頁、CRID 1390282679715495936、doi:10.11309/jssst.31.2_8、ISSN 0289-6540。)
- ^ Chakravarty, Manuel M. T. (14 February 1997). On the Massively Parallel Execution of Declarative Programs (Doctoral dissertation). Technical University of Berlin. 2015年2月26日閲覧。
In this context, the criterion for calling a programming language declarative is the existence of a clear, mathematically established correspondence between the language and mathematical logic such that a declarative semantics for the language can be based on the model or the proof theory (or both) of the logic.
- ^ "We say the operation is declarative if, whenever called with the same arguments, it returns the same results independent of any other computation state." P. Van Roy and S. Haridi (2001). コンピュータプログラミングの概念・技法・モデル. p.113.
- ^ 時間軸と何が起きたかを意識せずに宣言的に記述できる sonatard. (2019) 宣言的UI. p.37
- ^ 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)"
- ^ "programming concept where an ideal ... representation ... is kept in memory and synced with the “real” DOM by a library ... This approach enables the declarative API ... : You tell React what state you want the UI to be in, and it makes sure the DOM matches that state. This abstracts out the attribute manipulation, event handling ..." React. Virtual DOM and Internals.
- ^ "declarative UI ... works by conceptually regenerating the entire screen from scratch, then applying only the necessary changes." Thinking in Compose. Jetpack Compose.
- ^ "React provides a declarative API so that you don’t have to worry about exactly what changes on every update." React. Reconciliation.
- ^ 前回のViewの状態に依存せずに、最終的に描画されるViewを宣言的に記述できる sonatard. (2019) 宣言的UI. p.37
- ^ 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)"
参考文献
[編集]- Declarative language in The Free On-line Dictionary of Computing, Editor Denis Howe.
- Relational language in The Free On-line Dictionary of Computing, Editor Denis Howe.
- Logic programming in The Free On-line Dictionary of Computing, Editor Denis Howe.
- Functional programming in The Free On-line Dictionary of Computing, Editor Denis Howe.
- sonatard. (2019) 宣言的UI. builderscon Tokyo 2019
関連項目
[編集]外部リンク
[編集]- Frans Coenen. Characteristics of declarative programming languages. 1999.
- Olof Torgersson. A Note on Declarative Programming Paradigms and the Future of Definitional Programming. 1996.
- David Mertz. Declarative Programming with XML Stylesheet Language Transformations. 2001.
- Anders Norås. Declarative JavaScript programming. 2004-08-09.
- David Mertz. Advanced OOP: Declarative Programming and Mini-Languages. 2003-07-31.
- Narayanan Jayaratchagan. Declarative Programming in Java. 2004-04-21.
- N. Alex Rupp. Ruling Out: Rule Engines and Declarative Programming Come to Java. 2004-08-19.