多型の目的は何ですか?
ポリモーフィズムは、型の等価性の条件を緩和することにより、(重要な)静的型の安全性を失うことなく、静的型システムをより柔軟にします。型エラーが含まれていない場合にのみプログラムが実行されるという証拠は残っています。
ポリモーフィック関数またはデータ型は、幅広いシナリオで使用できるため、モノモーフィック関数またはデータ型よりも一般的です。この意味で、ポリモーフィズムは、厳密に型指定された言語における一般化の概念を表しています。
これは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 {}
);
サブタイプ多型
他の回答はすでにサブタイプ多型をカバーしているので、私はそれをスキップします。
構造的多型(別名Strutrualサブタイピング)
構造ポリモーフィズムでは、異なるタイプは同等であり、同じ方法で同じ構造が含まれている場合、一方のタイプにはもう一方のタイプのすべてのプロパティがありますが、追加のプロパティが含まれる場合があります。そうは言っても、構造多型はコンパイル時のダックタイピングであり、確かにいくつかの追加の型安全性を提供します。しかし、いくつかのプロパティを共有しているという理由だけで2つの値が同じ型であると主張することにより、値の意味レベルは完全に無視されます。
const weight = {value: 90, foo: true};
const speed = {value: 90, foo: false, bar: [1, 2, 3]};
残念ながら、speed
はのサブタイプと見なされweight
、value
プロパティを比較するとすぐに、リンゴとオレンジを事実上比較しています。