Тип стирання - це добре
Дотримуймося фактів
Наразі багато відповідей надмірно стосуються користувача Twitter. Корисно зосереджуватися на повідомленнях, а не на месенджері. Існує досить послідовне повідомлення навіть із лише згаданими на даний момент уривками:
Забавно, коли користувачі Java скаржаться на стирання типів, і це єдине, що Java отримала правильно, ігноруючи все те, що вона помилилася.
Я отримую величезні переваги (наприклад, параметричність) і нульову вартість (передбачувана вартість - це межа уяви).
new T - зламана програма. Це ізоморфно твердженню, що "всі положення є істинними". Мені це не до душі.
Мета: розумні програми
Ці твіти відображають точку зору, яку цікавить не те, чи можемо ми змусити машину щось робити , а більше, чи можемо ми міркувати, що машина зробить те, що ми насправді хочемо. Вагомі міркування є доказом. Докази можуть бути вказані у формальній нотації або щось менш формальне. Незалежно від мови специфікації, вони повинні бути чіткими і суворими. Неформальні специфікації неможливо правильно структурувати, але часто мають недоліки в практичному програмуванні. У підсумку ми отримуємо такі виправлення, як автоматизовані та дослідницькі тести, щоб компенсувати проблеми, які ми маємо, за допомогою неформальних міркувань. Це не означає, що тестування є внутрішньо поганою ідеєю, але цитований користувач Twitter припускає, що існує набагато кращий спосіб.
Отже, наша мета - мати правильні програми, про які ми можемо чітко і суворо міркувати таким чином, що відповідає тому, як машина насправді виконуватиме програму. Однак це не єдина мета. Ми також хочемо, щоб наша логіка мала ступінь виразності. Наприклад, існує лише стільки, що ми можемо висловити за допомогою логічної пропозиції. Приємно мати універсальну (∀) та екзистенційну (∃) кількісну оцінку з чогось типу логіки першого порядку.
Використання систем типу для міркувань
Ці цілі можуть бути дуже добре вирішені системами типів. Це особливо ясно через листування Каррі-Говарда . Ця відповідність часто виражається з такою аналогією: типи стосуються програм, як теореми - доказів.
Це листування є дещо глибоким. Ми можемо приймати логічні вирази та перекладати їх за допомогою відповідності типам. Тоді, якщо у нас є програма з компіляцією того самого типу, ми довели, що логічний вираз є універсальним істинним (тавтологія). Це пов’язано з тим, що листування є двостороннім. Перетворення між світом типу / програми та теореми / доказу є механічним, і в багатьох випадках може бути автоматизованим.
Каррі-Говард чудово розігрує те, що ми хотіли б зробити зі специфікаціями програми.
Чи корисні системи типів у Java?
Навіть маючи розуміння Каррі-Говарда, деяким людям легко заперечити значення системи типу, коли вона є
- надзвичайно важко працювати
- відповідає (через Каррі-Говарда) логіці з обмеженою виразністю
- порушено (що потрапляє до характеристики систем як "слабких" або "сильних").
Що стосується першого пункту, можливо, IDE роблять систему типів Java досить простою для роботи (це дуже суб'єктивно).
Що стосується другого пункту, Java, як правило, майже відповідає логіці першого порядку. Дженерики використовують систему типів, еквівалентну універсальній кількісній оцінці. На жаль, узагальнюючі символи дають нам лише незначну частину екзистенціальної кількісної оцінки. Але універсальна кількісна оцінка - досить хороший початок. Приємно мати можливість сказати, що функції для List<A>
роботи працюють універсально для всіх можливих списків, оскільки A абсолютно не обмежена. Це призводить до того, про що говорить користувач Twitter щодо "параметричності".
Часто цитувана стаття про параметричність - це теореми Філіпа Уодлера безкоштовно! . Що цікаво у цій роботі, так це те, що лише лише підпис типу ми можемо довести кілька дуже цікавих інваріантів. Якби ми писали автоматизовані тести для цих інваріантів, ми б дуже витрачали свій час. Наприклад, для List<A>
, від підпису типу лише дляflatten
<A> List<A> flatten(List<List<A>> nestedLists);
ми можемо це міркувати
flatten(nestedList.map(l -> l.map(any_function)))
≡ flatten(nestList).map(any_function)
Це простий приклад, і ви, напевно, можете міркувати про це неофіційно, але ще приємніше, коли ми отримуємо такі докази офіційно безкоштовно із системи типів і перевіряємо компілятором.
Не стирання може призвести до зловживань
З точки зору реалізації мови, загальні засоби Java (які відповідають універсальним типам) дуже сильно впливають на параметричність, яка використовується для отримання доказів того, що роблять наші програми. Це стосується третьої згаданої проблеми. Усі ці здобутки доказів і правильності вимагають звукової системи, реалізованої без дефектів. Java безумовно має деякі мовні особливості, які дозволяють нам зруйнувати наші міркування. До них належать, але не обмежуються:
- побічні ефекти із зовнішньою системою
- рефлексія
Нестирані дженерики багато в чому пов’язані з рефлексією. Без стирання існує інформація про час виконання, яка передається разом із реалізацією, яку ми можемо використовувати для розробки наших алгоритмів. Це означає, що статично, коли ми роздумуємо про програми, ми не маємо повної картини. Роздуми серйозно загрожують правильності будь-яких доказів, про які ми аргументуємо статично. Не випадково відображення також призводить до різноманітних хитрих дефектів.
Отже, якими способами незмиті дженерики можуть бути "корисними"? Давайте розглянемо використання, згадане в твіті:
<T> T broken { return new T(); }
Що станеться, якщо у T немає конструктора no-arg? У деяких мовах те, що ви отримуєте, є нульовим. Або, можливо, ви пропускаєте нульове значення і переходите безпосередньо до створення винятку (до якого нульові значення все одно призводять). Оскільки наша мова Тьюрінга повна, неможливо міркувати про те, які виклики broken
включатимуть "безпечні" типи з конструкторами no-arg, а які ні. Ми втратили впевненість у тому, що наша програма працює універсально.
Стирання означає, що ми обгрунтували (тож давайте стермо)
Отже, якщо ми хочемо міркувати щодо наших програм, нам настійно рекомендується не використовувати мовні функції, які сильно загрожують нашим міркуванням. Як тільки ми це робимо, то чому б просто не скинути типи під час виконання? Вони не потрібні. Ми можемо досягти певної ефективності та простоти із задоволенням, що жоден привід не провалиться або що методи можуть бути відсутніми при виклику.
Стирання спонукає до міркувань.