web-dev-qa-db-ja.com

参照の透明性とは何ですか?

用語は何ですか 参照の透明性 平均? 「equalsをequalsに置き換えることができる」という説明を聞いたことがありますが、これは説明が不十分なようです。

261
Claudiu

「参照の透明性」という用語は、論理と数学の方法に基づいて自然言語の構成要素、ステートメント、および引数を分析する哲学の分岐である 分析哲学 に由来します。言い換えれば、それは私たちが呼ぶものにコンピュータサイエンスの外で最も近い主題です プログラミング言語のセマンティクス 。哲学者 Willard Quine は、参照の透明性の概念を開始する責任がありましたが、バートランドラッセルとアルフレッドホワイトヘッドのアプローチにも暗黙的でした。

基本的に、「参照の透明性」は非常にシンプルで明確なアイデアです。 「リファレント」という用語は、分析哲学で、表現が参照するものについて言及するために使用されます。これは、プログラミング言語のセマンティクスで「意味」または「表示」が意味するものとほぼ同じです。アンドリュー・バーケットの例を使用すると( ブログ投稿 )、「スコットランドの首都」という用語はエジンバラの都市を指します。これは「リファレント」の簡単な例です。

が同じエンティティを参照する別の用語でそのコンテキスト内の用語を置き換えても意味が変わらない場合、文のコンテキストは「参照透過的」です。 。例えば

スコットランド議会はスコットランドの首都で開催されます。

と同じ意味

スコットランド議会はエジンバラで開催されます。

したがって、「スコットランド議会は...で会う」という文脈は、参照的に透明な文脈です。 「スコットランドの首都」を意味を変えずに「エディンバラ」に置き換えることができます。別の言い方をすれば、コンテキストは用語が何を指しているかだけを気にし、それ以外は何も気にしません。それは、コンテキストが「参照透過的」であるという意味です。

一方、文では、

エディンバラは1999年以来スコットランドの首都です。

そのような交換はできません。そうした場合、「エジンバラは1999年以来エジンバラになっています」と言うことになります。これは言うまでもなく、元の文と同じ意味を伝えません。したがって、「エジンバラは1999年以来...」というコンテキストは、参照的に不透明(参照的に透明の反対)であるように思われます。どうやらこの用語が指すものよりも何かを気にします。それは何ですか?

「スコットランドの首都」のようなものは確定用語と呼ばれ、長い間論理学者や哲学者に無駄な頭痛を与えませんでした。ラッセルとクインは、それらを実際には「参照」ではないと言って整理しました。つまり、上記の例がエンティティを参照するために使用されていると考えるのは誤りです。 「エジンバラは1999年以来スコットランドの首都であった」と理解する正しい方法は

スコットランドには1999年以来首都があり、その首都はエジンバラです。

この文章はナッツっぽいものに変えることはできません。問題が解決しました! Quineのポイントは、自然言語は実用的で使いやすいように作られているため、乱雑、または少なくとも複雑であると言うことでしたが、哲学者や論理学者はそれらを正しい方法で理解することで明確にする必要があります。参照透過性は、そのような意味の明確さをもたらすために使用されるツールです。

これはすべてプログラミングと何の関係がありますか?それほど多くはありませんが、実際には。前述したように、参照透過性は、言語の理解、つまりmeaningの割り当てに使用されるツールです。 Christopher Strachey は、プログラミング言語のセマンティクスの分野を設立し、意味の研究に使用しました。彼の基礎論文「 プログラミング言語の基本概念 」はWebで入手できます。それは美しい紙であり、誰もがそれを読んで理解することができます。だから、そうしてください。あなたは非常に啓発されます。彼はこの段落で「参照の透明性」という用語を紹介しています。

式の最も有用なプロパティの1つは、Quine参照透過性によって呼び出されるものです。本質的に、これは、部分式を含む式の値を見つけたい場合、その部分式について知る必要があるのはその値だけであることを意味します。内部構造、コンポーネントの数と性質、評価される順序、または書き込まれるインクの色など、サブ式の他の機能は、メインの値とは無関係です。表現。

「本質的に」の使用は、Stracheyが簡単な用語で説明するためにそれを言い換えていることを示唆しています。機能的なプログラマは、この段落を独自の方法で理解しているようです。論文には「参照の透明性」という他の9つの発生がありますが、他のいずれについても気にしないようです。実際、Stracheyの論文全体は、命令型プログラミング言語の意味を説明することに専念しています。しかし、今日、関数型プログラマーは、命令型プログラミング言語はnot参照的に透過的ではないと主張しています。 StracheyはGraveで曲がります。

状況を救うことができます。私たちは、自然言語は実用的に便利になるように作られているため、「厄介な、または少なくとも複雑な」と述べました。プログラミング言語も同じ方法です。これらは、実用に便利なように作られているため、「乱雑、または少なくとも複雑」です。それは彼らが私たちを混乱させる必要があるという意味ではありません。意味を明確にするために、参照的に透明なメタ言語を使用して、正しい方法で理解する必要があります。私が引用した論文で、Stracheyはまさにそれを行っています。彼は命令型プログラミング言語の意味を、それらを基本的な概念に分解することで説明します。彼の分析の重要な部分は、プログラミング言語の式にはl-valuesr-values。 Stracheyの論文以前は、これは理解されておらず、混乱が最高でした。今日、Cの定義はそれを日常的に言及しており、すべてのCプログラマーはその区別を理解しています。 (他の言語のプログラマーがそれを等しくよく理解しているかどうかは言うのが難しいです。)

QuineとStracheyは、何らかの形式のコンテキスト依存を含む言語構成の意味に関心がありました。たとえば、「エジンバラは1999年以来スコットランドの首都でした」という例は、「スコットランドの首都」が考慮されている時間に依存するという事実を意味します。このようなコンテキスト依存は、自然言語とプログラミング言語の両方で現実です。関数型プログラミングの場合でも、自由変数およびバインド変数は、それらが現れるコンテキストに関して解釈されます。あらゆる種類のコンテキスト依存関係は、何らかの方法で参照透過性をブロックします。用語が依存するコンテキストに関係なく用語の意味を理解しようとすると、再び混乱することになります。クインは、モーダルロジックの意味に関心がありました。彼は、 モーダルロジック は参照的に不透明であり、参照的に透明なフレームワークに変換することでクリーンアップする必要があると考えました(たとえば、必要性を証明可能性と見なす)。彼はこの議論を大きく失った。論理学者も哲学者も、クリプキの可能性のある世界のセマンティクスが完全に適切であることを発見しました。同様の状況は、命令型プログラミングにも当てはまります。 Stracheyが説明する状態依存性とReynoldsが説明するストア依存性(Kripkeの可能な世界のセマンティクスと同様の方法で)は完全に適切です。関数型プログラマーは、この研究の多くを知りません。参照の透明性に関する彼らのアイデアは、塩の大きな粒で取られるべきです。

[追記:上記の例は、「スコットランドの首都」などの単純なフレーズには複数のレベルの意味があることを示しています。あるレベルでは、現在の資本について話しているかもしれません。別のレベルでは、スコットランドが時間の経過とともに持っていたかもしれないすべての可能な首都について話しているかもしれません。特定のコンテキストを「ズームイン」および「ズームアウト」して、通常のプラクティスではすべてのコンテキストを非常に簡単に広げることができます。自然言語の効率は、その能力を活用しています。命令型プログラミング言語は、ほぼ同じ方法で効率的です。代入の右側で変数xを使用できます(r-value)特定の状態での値について話す。または、すべての状態にまたがるl-valueについて話します。このようなことで人々が混乱することはめったにありません。ただし、言語構成に固有の意味のすべての層を正確に説明できる場合とできない場合があります。そのような意味のすべての層は必ずしも「明白」ではなく、それらを適切に研究することは科学の問題です。しかし、このような階層化された意味を説明するための普通の人々の不明瞭さは、彼らがそれらについて混乱していることを意味しない。]

以下の個別の「追記」は、この議論を関数型プログラミングと命令型プログラミングの懸念に関連付けています

337
Uday Reddy

参照透過性は、関数型プログラミングで一般的に使用される用語であり、関数と入力値が与えられると、常に同じ出力を受け取ることを意味します。つまり、関数で使用される外部状態はありません。

参照透過関数の例を次に示します。

int plusOne(int x)
{
  return x+1;
}

参照透過関数では、入力と関数を指定すると、関数を呼び出す代わりに値で置き換えることができます。したがって、パラメーター5でplusOneを呼び出す代わりに、6に置き換えることができます。

別の良い例は、一般的な数学です。関数と入力値を指定した数学では、常に同じ出力値にマップされます。 f(x) = x +1。したがって、数学の関数は参照的に透過的です。

この概念は研究者にとって重要です。なぜなら、参照透過的な関数を持っているとき、簡単な自動並列化とキャッシングに役立つからです。

参照透過性は、Haskellなどの関数型言語で常に使用されます。

-

対照的に、参照不透明度の概念があります。これは逆を意味します。関数を呼び出すと、常に同じ出力が生成されるとは限りません。

//global G
int G = 10;

int plusG(int x)
{//G can be modified externally returning different values.
  return x + G;
}

別の例は、オブジェクト指向プログラミング言語のメンバー関数です。メンバー関数は、通常、そのメンバー変数で動作するため、参照不透明です。ただし、メンバー関数はもちろん参照的に透過的です。

さらに別の例は、テキストファイルから読み取り、出力を印刷する関数です。この外部テキストファイルはいつでも変更できるため、関数は参照的に不透明になります。

128
Brian R. Bondy

参照透過関数は、その入力のみに依存する関数です。

87
Draemon

[これは、議論を関数型/命令型プログラミングの懸念に近づけるための、3月25日からの私の回答に対する追記です。]

参照の透明性に関する機能プログラマの考え方は、次の3つの点で標準的な概念とは異なるようです。

  • 哲学者/論理学者は「参照」、「表示」、「指定」、「bedeutung」(フレーゲのドイツ語)などの用語を使用しますが、機能プログラマーは「値」という用語を使用します。 (これは完全に彼らの行いではありません。Landin、Strachey、およびその子孫も参照/表示について話すために「値」という用語を使用していることに気づきます。LandinとStracheyが導入した用語の単純化であるかもしれませんが、単純な方法で使用した場合の大きな違い。)

  • 機能的プログラマーは、これらの「価値」はプログラミング言語の外側にあるのではなく、プログラミング言語の中にあると信じているようです。これを行う際に、彼らは哲学者とプログラミング言語意味論者の両方とは異なります。

  • 彼らは、これらの「価値」が評価によって得られることになっていると信じているようです。

たとえば、ウィキペディアの 参照透過性 の記事は、今朝こう述べています。

式は、プログラムの動作を変更せずにその値で置き換えることができる場合(つまり、同じ入力で同じ効果と出力を持つプログラムを生成できる)、参照透過的であると言われます。

これは、哲学者/論理学者が言うこととは完全に異なります。彼らは、そのコンテキスト内の式を同じものを参照する別のexpression(acoreferentialに置き換えることができる場合、コンテキストは参照または参照透過的であると言います式)。これらの哲学者/論理学者は誰ですか? FregeRussellWhiteheadCarnapQuine教会 その他無数。それぞれがそびえ立つ人物です。これらの論理学者の組み合わされた知的力は、控えめに言っても驚異的です。それらはすべて、正式な言語の外部に指示対象/表示が存在し、言語内の表現はそれらについてのみabout話すことができるという立場で全会一致です。そのため、言語内でできることは、ある表現を同じエンティティを参照する別の表現に置き換えることだけです。指示対象/表示自体は言語内に存在しません。なぜ機能的なプログラマーは、この確立された伝統から逸脱するのですか?

プログラミング言語の意味論者がそれらを誤解させたかもしれないと推測するかもしれません。しかし、彼らはしませんでした。

ランディン

(a)各式にはネストされた部分式構造があり、(b)各部分式は何か(通常は数値、真理値、または数値関数)を表します、(c)式が示すもの、つまり「値」は、サブ式の値のみに依存し、サブ式の他のプロパティには依存しません。 [強調を追加]

Stoy

式に関して重要なのはその値だけであり、任意の部分式は値が等しい他の任意のものと置き換えることができます[強調の追加]。さらに、式の値は、特定の制限内では、発生するたびに同じです」。

鳥とワドラー

式の値は、その構成式の値(存在する場合)のみに依存し、これらの部分式は、同じ値を持つ他のに自由に置き換えることができます[強調の追加]。

そのため、振り返ってみると、「参照」/「表示」を「価値」に置き換えることで用語を簡素化するランディンとストラチーの努力は無頓着だったかもしれません。 「価値」を聞くとすぐに、それにつながる評価プロセスを考える誘惑があります。評価が「値」として生成するものを考えるのも同様に魅力的ですが、それが表示ではないことは非常に明らかかもしれません。それが、機能プログラマの目には「参照の透明性」という概念に出くわしたために集めたものです。しかし、初期の意味論者が語っていた「価値」は、ではなく評価の結果や関数の出力などです。それは用語の意味です。

表現のいわゆる「値」(古典哲学者の言説における「参照」または「表示」)を複雑な数学的/概念的なオブジェクトとして理解すると、あらゆる種類の可能性が開かれます。

  • Stracheyは、命令型プログラミング言語の変数をL-valuesとして解釈しました。これは、3月25日の回答で述べたように、構文の直接的な表現を持たない洗練された概念オブジェクトですプログラミング言語。
  • 彼はまた、そのような言語のコマンドを状態間関数として解釈しました。これは、構文内の「値」ではない複雑な数学オブジェクトの別のインスタンスです。
  • Cの副作用関数呼び出しでさえ、状態を状態と値のペアにマッピングする状態トランスフォーマーとして明確に定義された「値」を持っています(機能プログラマーの用語ではいわゆる「モナド」)。

関数型プログラマがこのような言語を「参照透過的」と呼ぶのを嫌がるということは、そのような複雑な数学的/概念的なオブジェクトを「値」として認めたがらないことを意味しているだけです。一方、彼らは、ステートトランスフォーマーを自分の好きな構文に入れて、「モナド」のような話題の単語でドレスアップしたときに、「値」と呼んでも完全に喜んでいるようです。 「参照の透明性」という考え方にある程度の一貫性があると認めたとしても、彼らは完全に矛盾していると言わざるを得ません。

これらの混乱がどのように発生したかについて、少しの歴史がいくらかの光を放つかもしれません。 1962年から1967年までの期間は、クリストファーストラチーにとって非常に集中的な時期でした。 1962年から65年の間、彼はモーリスウィルクスの研究助手としてアルバイトをし、CPLとして知られるようになったプログラミング言語を設計および実装しました。これは必須のプログラミング言語でしたが、強力な関数型プログラミング言語機能も備えていることを意図していました。コンサルタント会社のStracheyの従業員であったLandinは、プログラミング言語に対するStracheyの見解に大きな影響を与えました。画期的な1965年の論文「 Next 700 programming languages 」で、Landinは関数型プログラミング言語(denotative言語と呼ぶ)をabしげなく宣伝し、命令型プログラミング言語を"対照法(修辞"。続く議論では、StracheyがLandinの強い地位に疑問を投げかけていることがわかります。

... DLはすべての言語のサブセットを形成します。これらは興味深いサブセットですが、慣れていない限り使用するには不便です。現時点では命令とジャンプを含む言語で証明を構築する方法がわからないため、それらが必要です。 [強調を追加]

1965年、Stracheyはオックスフォードでリーダーの地位を取り、命令とジャンプの理論の開発に本質的にフルタイムで取り組んできたようです。 1967年までに、彼はコペンハーゲンのサマースクールで「 プログラミング言語の基本概念 」のコースで教えた理論を準備しました。講義ノートは公開されていたはずでしたが、「残念なことに、拡張編集のため、議事録は実現しませんでした。オックスフォードでのStracheyの作品の多くと同様に、この論文は私的流通に影響を与えました。」 ( マーティン・キャンベル・ケリー

Stracheyの著作を入手することの困難さは、人々が二次的な情報源と伝聞に頼っていて、混乱が広まっている可能性がある。しかし、今では「 Fundamental concept 」がウェブ上ですぐに利用できるようになったため、作業を推測する必要はありません。私たちはそれを読んで、Stracheyが何を意味するのかを自分で決めなければなりません。特に:

  • セクション3.2で、彼は「式」を扱い、「R値参照の透明性」について話しています。
  • 彼のセクション3.3は「コマンド」を扱っており、「L値参照透過性」について説明しています。
  • セクション3.4.5で、彼は「関数とルーチン」について話し、「R値コンテキストでのR値参照透過性の逸脱は、式をいくつかのコマンドとより単純な式に分解することによって排除する必要がある、またはこれはコメントの主題であることが判明しました。」

L値、R値、および命令型プログラマの概念的な世界に存在する他の複雑なオブジェクトの区別を理解しない「参照の透明性」の話は、根本的に間違っています。

71
Uday Reddy

式は、アルゴリズムを変更せずに値で置き換えることができる場合、参照的に透過的であり、同じ入力で同じ効果と出力を持つアルゴリズムを生成します。

23
CMS

参照透過関数は、数学関数のように機能する関数です。同じ入力が与えられると、常に同じ出力を生成します。これは、渡された状態が変更されておらず、関数に独自の状態がないことを意味します。

15
Barry Kelly

簡潔な説明が必要な人のために、私は危険を冒します(ただし、以下の開示をお読みください)。

プログラミング言語の参照透過性は、等式推論を促進します。参照透明性が高いほど、等式推論を簡単に実行できます。例えば。 (疑似)関数定義を使用して、

f x = x + x、

この定義の範囲内でf(foo)をfoo + fooに(安全に)置き換えることができ、この削減を実行できる場所にあまり多くの制約がなくても、簡単です。プログラミング言語の参照透過性の程度を示します。

たとえば、fooがCプログラミングの意味でx ++である場合、このリダクションを安全に実行できませんでした(つまり、このリダクションを実行する場合、元のプログラムと同じ結果にならないでしょう)。

実用的なプログラミング言語では、完全な参照の透明性は見られませんが、機能的なプログラマーはほとんどの場合よりも気にかけます(Haskellを参照してください。

(完全な開示:私は機能的なプログラマーですので、一番上の答えまでに、この説明を一粒で受け止めてください。)

8
chrisdornan

語源に興味がある場合(つまり、なぜこの概念にこの特定の名前があるのか​​)、このトピックについての私の ブログ投稿 をご覧ください。用語は哲学者/論理学者のクインに由来します。

8
Andrew Birkett
  1. Denotational-semanticsは、意味のあるvaluesを構成するドメインを構築することにより、言語をモデル化することに基づいています。
  2. 関数型プログラマーは、用語valueを使用して、言語の書き換えルールに基づいた計算の収束を記述します。その動作セマンティクス。

1では、問題の2つの言語が明確になっています。

  • モデル化されているもの、オブジェクト言語
  • モデリングの言語、メタ言語

2では、オブジェクトとメタ言語が近いため、混同される可能性があります。

言語の実装者として、私はこの区別を常に覚えておく必要があると感じています。

だから、レディ教授はこうしてあなたを言い換えることができます:-)

関数型プログラミングとセマンティクスのコンテキストでは、Referential Transparencyという用語は参照的に透過的ではありません。

4
Anuradha

次の答えは、論争の的となっている第1および第3の答えに追加され、適格であることを望みます。

式が何らかの指示対象を示しているか、参照していることを認めましょう。ただし、問題は、これらの参照対象を式自体の一部として同形的にエンコードできるかどうかであり、そのような式を「値」と呼びます。たとえば、リテラル数の値は算術式のセットのサブセットであり、真理値はブール式のセットのサブセットなどです。アイデアは式をその値(ある場合)に評価することです。そのため、単語「値」は、表現のセットの表示または識別された要素を指す場合があります。しかし、指示対象と値の間に同型(全単射)がある場合、それらは同じものであると言えます。 (これは、指示意味論の分野で証明されているように、指示対象と同型を定義することに注意する必要があります。3番目の回答への回答で言及された例を置くと、代数データ型定義_data Nat = Zero | Suc Nat_は対応しません自然数のセットに期待されるように。)

いくつかの四半期では「コンテキスト」としても知られている、穴のある式に対して_E[·]_を記述しましょう。 Cのような式の2つのコンテキスト例は、_[·]+1_と_[·]++_です。

式(穴のない)を取り、その意味(参照対象、表示など)を何らかの意味を提供するユニバースで提供する関数に対して_[[·]]_を記述しましょう。 (私は、表示的意味論の分野から表記法を借りています。)

Quineの定義を次のように形式的に適合させましょう:コンテキスト_E[·]_は、2つの式_E1_と_E2_(そこに穴はありません)が_[[E1]] = [[E2]]_(つまり式が同じ指示対象を示す/参照する場合は、_[[E[E1]]] = [[E[E2]]]_(つまり、_E1_または_E2_のいずれかで穴を埋めると、同じことを示す式になります参照先)。

ライプニッツのイコールをイコールに置き換えるルールは、通常、「if _E1 = E2_ then _E[E1] = E[E2]_」として表され、これは_E[·]_が関数であることを示します。関数(または、その関数を計算するプログラム)は、ソースからターゲットへのマッピングであるため、各ソース要素に対して最大で1つのターゲット要素が存在します。非決定的関数は誤った呼び名であり、関係、集合を提供する関数などのいずれかです。ライプニッツの規則で等号_=_が表示的である場合、二重括弧は単純に認められ、省略されます。したがって、参照透過コンテキストは関数です。そして、ライプニッツの規則は、等式推論の主要な要素であるため、等式推論は、参照の透明性に確実に関連しています。

_[[·]]_は式から表示までの関数ですが、式の制限されたサブセットとして理解される式から「値」までの関数である可能性があり、_[[·]]_は評価として理解できます。

ここで、_E1_が式であり、_E2_が値である場合、式、値、および評価の観点から参照透過性を定義するときに、ほとんどの人が意味するものと思います。しかし、このページの1番目と3番目の回答に示されているように、これは不正確な定義です。

_[·]++_などのコンテキストの問題は副作用ではありませんが、その値はその意味と同形でCで定義されていないということです。関数は値ではありません(関数へのポインターはそうです)が、関数型プログラミング言語では値です。 Landin、Strachey、および表示的セマンティクスの先駆者は、機能的な世界を使用して意味を提供するのに非常に賢明でした。

命令型Cライク言語の場合、関数[[·]] : Expression -> (State -> State x Value)を使用して式に(大まかに)セマンティクスを提供できます。

ValueExpressionのサブセットです。 Stateにはペア(識別子、値)が含まれます。セマンティック関数は式を取り、その意味として、現在の状態から更新された状態と値を持つペアに関数を提供します。たとえば、_[[x]]_は、現在の状態から、最初のコンポーネントが現在の状態で、2番目のコンポーネントがxの値であるペアへの関数です。対照的に、_[[x++]]_は、現在の状態から、最初のコンポーネントがxの値が増分される状態であり、2番目のコンポーネントがまさにその値であるペアへの関数です。この意味で、コンテキスト_[·]++_は、上記の定義を満たす場合に限り、参照的に透過的です。

関数型プログラマーは、式から値への関数として_[[·]]_を自然に回復するという意味で、参照透過性を使用する資格があると思います。関数は一流の値であり、状態は表示ではなく値でもあります。状態モナドは(部分的に)状態を渡す(またはスレッド化する)ためのクリーンなメカニズムです。

3
user5998940

この「意味」の概念は、観察者の心の中で起こるものであることに注意してください。したがって、同じ「参照」は、人によって異なることを意味します。そのため、たとえば、ウィキペディアにエジンバラの曖昧さ回避ページがあります。

プログラミングのコンテキストで現れる可能性のある関連する問題は、多態性である可能性があります。

そして、多相の特殊なケース(またはキャスト)の名前を付ける必要があります。ここでは、目的のために、異なる多相のケースが(単に類似しているのとは対照的に)意味的に同等です。整数型、複合型、または他のさまざまな型のいずれかを使用して、多相的に処理できます)。

2
rdm

参照の透明性は、次のように簡単に説明できます。

  • どのコンテキストでも常に同じ結果に評価される式 [1]
  • 同じパラメーターを2回指定した場合、関数は同じ結果を2回生成する必要があります [2]

たとえば、プログラミング言語Haskellは純粋な関数型言語です。つまり、参照的に透過的です。

0
Can