ポリモーフィズムの目的は何ですか?
ポリモーフィズムは、型の等価性の条件を緩和することで、(重大な) 静的型の安全性を失うことなく、静的型システムをより柔軟にします。型エラーが含まれていない場合にのみプログラムが実行されるという証拠は残っています。
多相関数またはデータ型は、より幅広いシナリオで使用できるため、単相関数またはデータ型よりも一般的です。この意味で、ポリモーフィズムは、厳密に型付けされた言語における一般化の考え方を表しています。
これはJavascriptにどのように適用されますか?
Javascript の動的型システムは脆弱です。このような型システムは、1 つの型のみを含む厳密な型システムと同等です。このような型は、巨大な共用体型 (疑似構文) と考えることができます。
type T =
| Undefined
| Null
| Number
| String
| Boolean
| Symbol
| Object
| Array
| Map
| ...
すべての値は、実行時にこれらの型の代替の 1 つに関連付けられます。また、Javascript は型付けが弱いため、すべての値の型が何度でも変更される可能性があります。
型理論の観点から、型が 1 つしかないことを考えると、Javascript の型システムにはポリモーフィズムの概念がないと確信を持って言えます。代わりに、ダックタイピングと暗黙の型強制があります。
しかし、だからと言って、プログラムの型について考えるのをやめるべきではありません。Javascript には型がないため、コーディング プロセス中に型を推測する必要があります。つまり、プログラムを見るとすぐに、アルゴリズムだけでなく、基礎となる (おそらくポリモーフィックな) 型も認識しなければなりません。これらのタイプは、より信頼性が高く、より堅牢なプログラムを構築するのに役立ちます。
これを適切に行うために、ポリモーフィズムの最も一般的な症状の概要を説明します。
パラメトリック ポリモーフィズム (別名ジェネリック)
パラメトリック ポリモーフィズムでは、型はまったく問題にならないため、さまざまな型が交換可能であると言います。パラメトリック ポリモーフィック型の 1 つまたは複数のパラメーターを定義する関数は、対応する引数について何も認識してはいけませんが、それらをすべて同じように扱う必要があります。このような関数は、データの一部ではない引数のプロパティでのみ機能するため、これは非常に制限的です。
// parametric polymorphic functions
const id = x => x;
id(1); // 1
id("foo"); // "foo"
const k = x => y => x;
const k_ = x => y => y;
k(1) ("foo"); // 1
k_(1) ("foo"); // "foo"
const append = x => xs => xs.concat([x]);
append(3) ([1, 2]); // [1, 2, 3]
append("c") (["a", "b"]); // ["a", "b", "c"]
アドホック ポリモーフィズム (別名オーバーロード)
アドホック ポリモーフィズムとは、さまざまな型が特定の目的でのみ同等であるということです。この意味で同等であるためには、型はその目的に固有の関数のセットを実装する必要があります。アドホック ポリモーフィック型の 1 つ以上のパラメーターを定義する関数は、各引数に関連付けられている関数のセットを知る必要があります。
アドホック ポリモーフィズムにより、関数はより大きな型のドメインと互換性を持つようになります。次の例は、「マップオーバー」の目的と、型がこの制約を実装する方法を示しています。関数のセットの代わりに、「マップ可能」制約には単一のmap
関数のみが含まれます。
// Option type
class Option {
cata(pattern, option) {
return pattern[option.constructor.name](option.x);
}
map(f, opt) {
return this.cata({Some: x => new Some(f(x)), None: () => this}, opt);
}
};
class Some extends Option {
constructor(x) {
super(x);
this.x = x;
}
};
class None extends Option {
constructor() {
super();
}
};
// ad-hoc polymorphic function
const map = f => t => t.map(f, t);
// helper/data
const sqr = x => x * x;
const xs = [1, 2, 3];
const x = new Some(5);
const y = new None();
// application
console.log(
map(sqr) (xs) // [1, 4, 9]
);
console.log(
map(sqr) (x) // Some {x: 25}
);
console.log(
map(sqr) (y) // None {}
);
サブタイプ多型
他の回答はすでにサブタイプ多型をカバーしているため、スキップします。
構造的多型 (別名、構造的サブタイピング)
構造ポリモーフィズムは、異なるタイプが同じ構造を含む場合、1 つのタイプが他のタイプのすべてのプロパティを持ちますが、追加のプロパティを含む場合があることを意味します。そうは言っても、構造的ポリモーフィズムはコンパイル時のダックタイピングであり、確かに追加の型安全性を提供します。しかし、いくつかのプロパティを共有しているという理由だけで 2 つの値が同じ型であると主張することで、値のセマンティック レベルを完全に無視しています。
const weight = {value: 90, foo: true};
const speed = {value: 90, foo: false, bar: [1, 2, 3]};
残念ながら、speed
は のサブタイプと見なされ、プロパティをweight
比較するとすぐに、value
実質的にリンゴとオレンジを比較しています。