Тип Стирание - это хорошо
Будем придерживаться фактов
Многие ответы на данный момент чрезмерно связаны с пользователем 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 нет конструктора без аргументов? На некоторых языках вы получаете нулевое значение. Или, возможно, вы пропустите нулевое значение и сразу перейдете к возбуждению исключения (к чему все равно приводят нулевые значения). Поскольку наш язык является полным по Тьюрингу, невозможно понять, какие вызовы broken
будут включать «безопасные» типы с конструкторами без аргументов, а какие - нет. Мы потеряли уверенность в том, что наша программа работает повсеместно.
Стирание означает, что мы рассудили (так что давайте стираем)
Поэтому, если мы хотим рассуждать о наших программах, мы настоятельно не рекомендуем использовать языковые функции, которые сильно угрожают нашим рассуждениям. Как только мы это сделаем, почему бы просто не отбросить типы во время выполнения? Они не нужны. Мы можем получить некоторую эффективность и простоту с удовлетворением, что ни одно приведение не завершится ошибкой или что методы могут отсутствовать при вызове.
Стирание побуждает к рассуждению.