1

これは哲学に近いかもしれませんが、質問するのに適切な場所だと思いました.

ID のリストを作成する関数があるとします。これらの識別子はアプリケーションの内部でのみ使用されるため、ここで ES2015 を使用してもかまいませんSymbol()

私の問題は、技術的には、シンボルを要求すると、JS ランタイムが一意の識別子 (乱数? メモリ アドレス? 不明) を作成し、衝突を防ぐためにグローバル状態にアクセスする必要があると思います。よくわからないのは、「技術的に」という言葉のせいです。API が提示する数学的抽象化を破るのにこれが十分であるかどうかは、(やはり哲学的な観点から)わかりません。

tl;dr:これが例です--

function sentinelToSymbol(x) {
  if (x === -1) return Symbol();
  return x;
}

この関数は純粋ですか?

4

2 に答える 2

3

そうではありませんが、実際には問題ではないかもしれません。

表面上は(foo) => Symbol(foo)純粋に見えます。ランタイム副作用のある操作を実行する場合がありますSymbol()が、同じパラメーターで同時に呼び出したとしても、それらが表示されることはありません。ただし、同じ引数を指定して呼び出すSymbolと、同じ値が返されることはありません。これは、主な基準の 1 つです (以下の #2)。

MDN ページから:

Symbol("foo") は文字列 "foo" をシンボルに変換しないことに注意してください。毎回新しいシンボルを作成します。

Symbol("foo") === Symbol("foo"); // false

副作用だけを見ると、(foo) => Symbol(foo)純粋です(ランタイム以上)。

ただし、純粋関数はより多くの基準を満たす必要があります。ウィキペディアから

純粋に機能的な関数 (または式) には、副作用 (メモリまたは I/O) がありません。これは、純粋関数にはいくつかの有用なプロパティがあり、その多くをコードの最適化に使用できることを意味します。

  • 純粋な式の結果が使用されていない場合は、他の式に影響を与えることなく削除できます。
  • 純粋な関数が副作用を引き起こさない引数で呼び出された場合、結果はその引数リストに関して一定です (参照透過性と呼ばれることもあります)。つまり、純粋な関数が同じ引数で再度呼び出された場合、同じ結果が返されます。返されます (これにより、メモ化などのキャッシュの最適化が有効になります)。
  • 2 つの純粋な式の間にデータの依存関係がない場合、それらの順序を逆にするか、並列で実行でき、互いに干渉することはありません (つまり、純粋な式の評価はスレッドセーフです)。
  • 言語全体で副作用が許容されない場合は、任意の評価戦略を使用できます。これにより、コンパイラーは、プログラム内の式の評価を並べ替えたり、組み合わせたりする自由が与えられます (たとえば、森林伐採を使用)。

そのリストの序文は JavaScript のすべてを除外していると主張することができます。なぜなら、操作によってメモリが割り当てられたり、内部構造が更新されたりする可能性があるからです。最も厳密な解釈では、JS は決して純粋ではありません。それはあまり面白くも役に立たないので...

この関数は基準 1 を満たしています。結果を無視し、外部の観察者と同じです(foo) => Symbol(foo)(foo) => ()

基準 2 は、さらに問題を引き起こします。を考えるbar = (foo) => Symbol(foo)bar('xyz') !== bar('xyz')Symbolその要件をまったく満たしていません。を呼び出すたびに、一意のインスタンスが返されることが保証されますSymbol

先に進みますが、基準 3 は問題ありません。競合することなく (並列) 異なるスレッドから呼び出すことができ、呼び出さSymbolれる順序は関係ありません。

最後に、基準 #4 は、直接的な要件というよりも重要であり、簡単に満たすことができます (JS ランタイムは、進行中にすべてをシャッフルします)。

したがって:

  • 厳密に言えば、JS には純粋なものはありません。
  • Symbol()は間違いなく純粋ではないため、この例もそうではありません。
  • メモ化ではなく副作用だけを気にするのであれば、この例はそれらの基準を満たしています。
于 2016-08-01T18:46:13.223 に答える
0

はい、この関数は不純です: sentinelToSymbol(-1) !== sentinelToSymbol(-1). ここでは、純粋な関数に対して等しいことが期待されます。

ただし、オブジェクト ID を持つ言語で参照透過性の概念を使用する場合は、定義を少し緩めた方がよいかもしれません。と考えるとfunction x() { return []; }、純正ですか?明らかにx() !== x()、ただし、定数関数のように、入力に関係なく、関数は常に空の配列を返します。したがって、ここで定義しなければならないのは、私たちの言語における価値の平等です。この===演算子は、ここでは最適ではない可能性があります (単に を考慮してくださいNaN)。配列に同じ要素が含まれている場合、配列は互いに等しいですか? どこかで変異していない限り、おそらくそうです。

したがって、シンボルについて同じ質問に答える必要があります。シンボルは不変であるため、その部分が簡単になります。.toString()[[Description]] 値 (または) で等しいと見なすことsentinelToSymbolができるので、その定義では純粋です。

しかし、ほとんどの言語には、参照透過性を破ることができる関数があります。たとえば、How to print memory address of a list in Haskell を参照してください。JavaScript では、これは===他の点では同等のオブジェクトで使用されます。そして、シンボルのアイデンティティを検査するため、シンボルをプロパティとして使用します。そのため、プログラムでそのような操作を使用しない場合 (または少なくとも外部から監視できない場合)、関数の純粋性を主張し、それを使用してプログラムについて説明することができます。

于 2016-08-01T18:49:45.303 に答える