Яка мета поліморфізму?
Поліморфізм робить систему статичного типу більш гнучкою, не втрачаючи (значну) безпеку статичного типу, послаблюючи умови для еквівалентності типу. Доказом залишається те, що програма працюватиме лише в тому випадку, якщо вона не містить помилок типу.
Поліморфна функція або тип даних є більш загальними, ніж мономорфні, оскільки вони можуть бути використані в більш широкому діапазоні сценаріїв. У цьому сенсі поліморфізм представляє ідею узагальнення в строго типових мовах.
Як це стосується Javascript?
Javascript має слабку, динамічну систему типу. Така система типів еквівалентна системі суворого типу, що містить лише один тип. Ми можемо уявити такий тип як величезний тип об’єднання (псевдосинтаксис):
type T =
| Undefined
| Null
| Number
| String
| Boolean
| Symbol
| Object
| Array
| Map
| ...
Кожне значення буде пов'язане з одним із цих варіантів типу під час виконання. А оскільки Javascript слабко набирається, кожне значення може змінювати свій тип будь-яку кількість разів.
Якщо взяти теоретичну перспективу типу і врахувати, що існує лише один тип, можна з упевненістю сказати, що система типів Javascript не має поняття поліморфізму. Натомість ми маємо набір качок та примусовий примус.
Але це не повинно заважати нам думати про типи в наших програмах. Через відсутність типів у Javascript нам потрібно зробити висновок про них під час процесу кодування. Наш розум повинен стати на захист відсутнього компілятора, тобто, як тільки ми подивимося на програму, ми повинні розпізнати не тільки алгоритми, але й основні (можливо, поліморфні) типи. Ці типи допоможуть нам створити більш надійні та надійні програми.
Щоб зробити це належним чином, я збираюся дати вам огляд найпоширеніших проявів поліморфізму.
Параметричний поліморфізм (він же дженерики)
Параметричний поліморфізм говорить, що різні типи взаємозамінні, оскільки типи взагалі не мають значення. Функція, яка визначає один або кілька параметрів параметричного поліморфного типу, не повинна знати нічого про відповідні аргументи, але обробляти їх однаково, оскільки їх можна прийняти до будь-якого типу. Це досить обмежує, оскільки така функція може працювати лише з тими властивостями своїх аргументів, які не є частиною їх даних:
const id = x => x;
id(1);
id("foo");
const k = x => y => x;
const k_ = x => y => y;
k(1) ("foo");
k_(1) ("foo");
const append = x => xs => xs.concat([x]);
append(3) ([1, 2]);
append("c") (["a", "b"]);
Спеціальний поліморфізм (він же перевантаження)
Спеціальний поліморфізм говорить, що різні типи еквівалентні лише для певної мети. Щоб бути еквівалентним у цьому сенсі, тип повинен реалізувати набір функцій, специфічних для цієї мети. Тоді функція, яка визначає один або кілька параметрів спеціального поліморфного типу, повинна знати, які набори функцій асоційовані з кожним з її аргументів.
Спеціальний поліморфізм робить функцію сумісною з великим доменом типів. Наступний приклад ілюструє мету "map-over" і те, як типи можуть реалізувати це обмеження. Замість набору функцій обмеження "mappable" включає лише одну map
функцію:
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();
}
};
const map = f => t => t.map(f, t);
const sqr = x => x * x;
const xs = [1, 2, 3];
const x = new Some(5);
const y = new None();
console.log(
map(sqr) (xs)
);
console.log(
map(sqr) (x)
);
console.log(
map(sqr) (y)
);
Підтип поліморфізму
Оскільки інші відповіді вже стосуються політипу підтипу, я пропускаю його.
Структурний поліморфізм (він же структурний підтип)
Структурний поліморфізм говорить, що різні типи еквівалентні, якщо вони містять однакову структуру таким чином, що один тип має всі властивості іншого, але може включати додаткові властивості. Тим не менш, структурний поліморфізм є типовим типом під час компіляції і, безсумнівно, пропонує деяку додаткову безпеку типу. Але, стверджуючи, що два значення одного типу лише тому, що вони мають деякі властивості, він повністю ігнорує семантичний рівень значень:
const weight = {value: 90, foo: true};
const speed = {value: 90, foo: false, bar: [1, 2, 3]};
На жаль, speed
це вважається підтипом, weight
і як тільки ми порівнюємо value
властивості, ми фактично порівнюємо яблука з апельсинами.