「宣言型プログラミング」の版間の差分
削除された内容 追加された内容
m clean up |
82chidnoels (会話 | 投稿記録) |
||
(同じ利用者による、間の8版が非表示) | |||
1行目:
{{プログラミング・パラダイム}}'''宣言型プログラミング'''({{lang-en-short|Declarative programming}})は、[[数学]]的または[[記号論理学|記号論理]]的な性質を表わしている総称的な[[プログラミングパラダイム]]である。[[命令型プログラミング]]と対をなしての[[プログラミング言語]]の分類用語としても扱われている。その書式は、[[式 (プログラミング)|式]]の計算構造を主に[[表示的意味論]]下のロジックで表現するものになり、[[式 (プログラミング)|式]]枠外の[[副作用 (プログラム)|副作用]]を伴なう[[制御フロー]]や[[自由変数と束縛変数|自由変数]]の多用などは排除されるようになる<ref>{{citation|last=Lloyd|first=J.W.|title=Practical Advantages of Declarative Programming}}</ref>。
宣言型言語は、[[ドメイン知識]]における”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>。
宣言的パラダイムは、[[関数型プログラミング|関数型]]、[[論理プログラミング|論理型]]、[[データフロープログラミング|データフロー]]などを包括し、[[問い合わせ言語|データベース問い合わせ言語]]、[[マークアップ言語]]、[[ドメイン固有言語]]、[[構成管理]]、[[正規表現]]などにも言及されており、[[並行計算]]との親和性も特筆されている<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>。
== 概要 ==
宣言型プログラミングは、現行式枠外の外部状態への代入[[コマンド (コンピュータ)|コマンド]]、および外部状態の現行式への影響([[副作用 (プログラム)|副作用]])といった命令的な性質を持たないパラダイムとして定義されている。命令的性質の[[文 (プログラミング)|ステートメント]]に対して、宣言的なプログラム基本文は[[式 (プログラミング)|式]]とされる。
コマンドと副作用を持つ命令的なオペレータ([[手続き]]・[[関数 (プログラミング)|関数]]・[[サブルーチン|ルーチン]])は、計算内容のリスト化とステップ単位解釈が必要になるので、これがhow to accomplish it(どうなすべきか)とされる。コマンドと副作用を持たない宣言的なオペレータは、その定義だけで計算内容を把握できるので、これがwhat to accomplish it(何をなすべきか)とされる。命令的オペレータを用いずに、宣言的オペレータを用いることが即ち宣言的なプログラムになる<ref name=":0" /><ref>「宣言的記述を行う高水準言語の主要なお題目は『どうやって計算するか(How)ではなく, 何を計算するか(What)を記述する』というものである.」 Chikayama (2014). [https://www.jstage.jst.go.jp/article/jssst/31/2/31_2_8/_pdf ''Software: Thirty Past Years and the Futures'']. コンピュータソフトウェア, Vol.31, No.2, p.9.</ref>。式は[[演算子|オペレータ]]、[[被演算子|オペランド]]、他の式、自己[[再帰]]式などの組み合わせになる。
宣言的パラダイムにあるべき特徴は以下のようになる。
# 計算を主に[[表示的意味論]]で記述する[[高水準言語]]
# [[副作用 (プログラム)|副作用]]を排除した[[参照透過性|参照透過]]なプログラム
# 計算そのものを計算対象にできるという[[高階論理|高階]]なプログラム
#[[数理論理学]]に準拠したプログラム<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]]|access-date=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>
[[式 (プログラミング)|式]]は単体で評価されるほか、引数に適用されて評価値(返り値)になる。式はほかへの引数にもなり、[[高階論理]]の式は他の式を引数にする。[[微分]]の[[導関数]]と同様に、式の返り値を式にすることもできる。引数や返り値にもできる式は、[[第一級オブジェクト]]と呼ばれる。
[[問い合わせ言語]]の[[SQL]]のクエリは宣言的とされるが、データベースの更新という[[副作用 (プログラム)|副作用]]が伴なわれる場合はそうとは言えない。[[論理プログラミング]]も通常の[[導出原理]]は宣言的であるが、その過程で[[知識表現]]の追加が行われる場合はそうと言えなくなる。[[関数型言語]]の多くは、コマンドと副作用の取り扱いも許容している命令型と宣言型の折衷になっている。純粋関数型言語は宣言的に徹しており、プログラム[[正当性 (計算機科学)|正当性]]の[[形式的検証]]を可能にしている。宣言型の専売特許である形式的検証に対して、命令型では[[クラス (コンピュータ)|クラス]]や[[オブジェクト (プログラミング)|オブジェクト]]を宣言的フレームワークに内包して局面的な宣言的オペレータにしてその動作を検証することがある。[[Java]]テストフレームワーク[[JUnit]]などが例である。
宣言型は[[並行プログラミング]]との親和性が高いことも特筆される。これは[[セマフォ]]や[[ミューテックス]]や読み書き[[ロック (計算機科学)|ロック]]などを駆使して[[同期 (計算機科学)|同期]]的な[[並行性]]を実現することが多い[[命令型プログラミング|命令型]]に対するアドバンテージである。宣言型は、式としてのプロセスを[[プロセス代数|代数]]として扱えるので、その代数を他のプロセスへの引数としての[[メッセージ (コンピュータ)|メッセージ]]にしつつ、部分計算や[[評価戦略]]を応用しての非同期な並行性を実現できる。
== 利点 ==
[[参照透過性|参照透過]]な計算式は、時と場所に左右されない抽象値にもなる<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>とするのが宣言型のアプローチである。この概念の実装例は[[クロージャ]]である。クロージャは、他の式や関数の束縛変数にされることが前提のレキシカルスコープ基準の[[無名関数]]である。
もう一つの実装例は、宣言的な[[関数オブジェクト]]である。そこでは処理+返り値の青写真になる不変部分と、引数で決定される処理+返り値の可変部分が明確に分離されている。宣言型は、[[段階的詳細化法|段階的詳細化]]した各要素を不変部分と可変部分の構成体にすることをアプローチする。これに準拠した技術の仮想[[Document Object Model|DOM]]は、[[XML]]や[[Html|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>。その不変部分は不変状態と同義になり、しばしば[[イミュータブル]]の概念で説明される。
▲=== 手続きの最適化 ===
[[グラフィカルユーザインタフェース#宣言的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.▼
宣言型は、次以降の式で確定される前方参照要素を残したままの抽象値の取り扱いをアプローチする。その実装例には[[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状態ならまだしも、多数の状態が相互作用する多数のオブジェクトから操作される場合、状態の予測は著しく困難になり、デバッグを含めたプログラミングが難しくなりうる。一方で宣言型プログラミングの場合、宣言部分は状態履歴を必要としないため、宣言部分の出力は常に明確である。
注意点として、状態は分離されているのであり、状態が消滅したわけではない。宣言型プログラミングの場合、light変数にてあるべきライトの状態 "ON"/"OFF" を保持しておき、現在の時刻に基づいてlight変数を切り替え、それが「廊下の電気は{light}である」という宣言に反映されて実際に廊下の電気がONになるというような形になる。light変数という状態は存在しており、これが宣言部分と分離され、宣言部分は最新のlight変数が示す今の電気があるべき状態のみを反映した(過去の電気状態履歴とは無関係な)形になっている
==宣言型言語の例==
宣言型に準拠したプログラミング
*[[JUnit]]▼
*論理
宣言型
*[[XSL Transformations|XSLT]] は、[[Extensible Markup Language|XML]]文書の変換のためのチューリング完全な宣言型言語
51 ⟶ 71行目:
*[[Nix Expression Language]]
*[[Dhall]]
宣言型を適用したフレームワーク各種
▲*[[JUnit]]
▲*関数: [[Erlang]]、[[Haskell]]、[[LISP]]
▲*論理: [[Prolog]]、[[Concurrent Prolog]]、[[Guarded Horn Clauses|GHC]]、[[:en:Mercury programming language|Mercury]]
▲*制約: [[Oz (プログラミング言語)|Oz]]、[[Constraint Handling Rules]]
▲* {{節リンク|グラフィカルユーザインタフェース|宣言的UI}}
== 脚注 ==
73 ⟶ 87行目:
== 関連項目 ==
*[[数理論理学]]
*[[参照透過性]]
*[[表示的意味論]]
*[[ドメイン固有言語]]
*[[マークアップ言語]]
▲**[[非手続き型言語]]
== 外部リンク ==
* Frans Coenen. [http://www.csc.liv.ac.uk/~frans/OldLectures/2CS24/declarative.html#detail Characteristics of declarative programming languages]. 1999.
|