空配列における every と some の件

出所は知らないんだけども、「JavaScript の [].every(x => true)true[].some(x => true)false を返す」という 話題 を見て、へーそうなんだ知らなかった、と思った。

知らなかったということはどこかでバグを仕込んでいそうである。

例えば次のようなコード。

const products: { name: string, inventory: number }[] = [
  { name: 'A', inventory: 1 },
  { name: 'B', inventory: 1 },
  { name: 'C', inventory: 3 },
];

const hasSoldout = products.some(x => x.inventory == 0);

console.log(hasSoldout ? '在庫切れの商品があります' : '在庫は十分です');

これは商品群の内、一つでも在庫数が0のものがあったら「在庫切れの商品があります」と表示するもので、products のいずれかの inventory を 0 にするとそのメッセージになる。

もし、products が、データベースから読み出される可変配列で、それが 0 件だった場合、上記の処理では「在庫は十分です」と表示されてしまう。

この場合、一般的に想定される業務(機能)要件的には「在庫は十分です」よりも「在庫切れの商品があります」の方が、(イレギュラーな事態に早く気付くことができるという点で)安全サイドに倒した Better な仕様だろう。

このように someevery だけでは業務(機能)要件を満たせないかもしれないと知る事ができたので良かった。

また、「在庫切れの商品があります」vs「在庫は十分です」は、"商品群がゼロ" というケースには対応できないと判断すべきで、そのような仕様が足りない(つまり if (products.length == 0) { が必要)という不足にも気付かせてくれた。

仮に some を使わず filter で同じことを実現しようとすると、

const hasSoldout = products.filter(x => x.inventory == 0).length > 0;

となるが、この場合も products が0件であれば hasSoldout は some と同じく false になるので Better な仕様の実現にはならない。

なのでどうあっても言語仕様の(集合論の?) some や every の返値がおかしいとはならず、機能仕様として空配列の時にどう振る舞うか?を決めるべき、という結論になると思う。

最後に ChatGPT(GPT-4)さんに聞いた結果を載せておきます。

Q: それは Vacuous truth という考え方に基づくものですか?

A: はい、その通りです。これは "Vacuous truth"、または "Empty truth" という概念に基づいています。

この概念は、ある集合が空の場合、その集合の全ての要素が任意の性質を持つと言える、というものです。
つまり、ある性質を持つ要素が一つもない(なぜなら要素自体が一つもないから)という理由で、
全ての要素がその性質を持つ、と考えます。

JavaScriptの every メソッドもこの原則に従います。
空の配列に対して every 関数が呼び出されると、
この関数は true を返します。
なぜなら、その配列には「与えられた関数に一致しない要素」が存在しないからです。

この原則は論理学や数学、そしてプログラミングのいくつかの分野で広く受け入れられています。