Причины удаления типов функций в Java 8

12

Я пытался понять, почему JDK 8 Lambda Expert Group (EG) решила не включать новый тип функции в язык программирования Java.

Просматривая список рассылки, я нашел ветку с обсуждением удаления типов функций .

Многие из утверждений для меня неоднозначны, возможно, из-за отсутствия контекста, а в некоторых случаях из-за моих ограниченных знаний о реализации систем типов.

Тем не менее, есть пара вопросов, которые, я считаю, я могу смело сформулировать на этом сайте, чтобы помочь мне лучше понять, что они имели в виду.

Я знаю, что могу задавать вопросы в списке рассылки, но ветка устарела, и все решения уже приняты, поэтому есть вероятность, что меня проигнорируют, прежде всего, увидев, что эти парни уже запаздывают со своими планами.

В своем ответе, чтобы поддержать удаление типов функций и одобрить использование типов SAM, Брайан Гетц говорит:

Нет овеществления. Была длинная тема о том, насколько полезно было бы преобразовывать типы функций. Без реализации, типы функций не работают.

Я не мог найти нить, которую он упоминает. Теперь я могу понять, что введение структурного функционального типа может подразумевать определенные сложности в Java, в основном системе номинальных типов, но я не могу понять, насколько параметризованные типы SAM отличаются с точки зрения реализации.

Разве они оба не подвержены одинаковым проблемам овеществления? Кто-нибудь понимает, как типы функций отличаются от параметризованных типов SAM с точки зрения реализации?

В другом комментарии Гетц говорит:

Существует два основных подхода к типированию: номинальный и структурный. Личность номинала основана на его имени; идентичность структурного типа основана на том, из чего он состоит (например, «tuple of int, int» или «function from int to float»). Большинство языков выбирают в основном номинальные или в основном структурные; не так много языков, в которых успешно сочетаются номинальная и структурная типизация, кроме «по краям». Java почти полностью номинальна (за некоторыми исключениями: массивы являются структурным типом, но в нижней части всегда есть номинальный тип элемента; дженерики также имеют сочетание номинального и структурного типов, и это на самом деле является частью источника многих жалоб людей на дженерики.) Привлечение структурной системы типов (типов функций) на Java ' Система номинального типа означает новые сложности и крайние случаи. Стоит ли преимущество типов функций?

Те из вас, кто имеет опыт внедрения систем типов. Вы знаете какие-либо примеры этих сложностей или крайних случаев, которые он упоминает здесь?

Честно говоря, меня смущают эти утверждения, когда я считаю, что такой язык программирования, как Scala, полностью основанный на JVM, имеет поддержку структурных типов, таких как функции и кортежи, даже с учетом проблем с реализацией базовой платформы.

Не поймите меня неправильно, я не говорю, что тип функции должен быть лучше, чем тип SAM. Я просто хочу понять, почему они приняли это решение.

edalorzo
источник
4
«Привлечение структурной системы типов (типов функций) в систему именных типов Java означает новую сложность» - это, скорее всего, означало сложности для пользователей, которым нужно больше учиться пользоваться языком . Что-то вроде простого и легкого в использовании Java становится сложным и трудным для изучения C ++ , и тому подобное. Члены списка рассылки, вероятно, могут сослаться на критику предыдущей «большой волны улучшений», одним из ярких примеров которой является Java 5 отстойная статья Клинтона Бегина из MyBatis
gnat
3
«Простой и легкий в использовании Java становится сложным и трудным для изучения C ++»: К сожалению, одна проблема с основными языками программирования состоит в том, что они должны сохранять обратную совместимость и, следовательно, они становятся слишком сложными. ИМХО (имея многолетний опыт работы с C ++ и Java), в этом утверждении есть что-то верное. Мне часто кажется, что Java должна быть заморожена, и вместо этого должен быть разработан новый язык. Но Oracle не может пойти на такой риск.
Джорджио
4
@edalorzo: Как показали Clojure, Groovy, Scala и другие языки, можно (1) определить новый язык (2) использовать все богатство библиотек и инструментов, уже написанных на Java, без нарушения обратной совместимости (3) У Java-программистов есть свобода выбора, хотят ли они остаться с Java, переключиться на другой язык или использовать оба. IMO, расширение существующего языка на неопределенный срок - это стратегия удержания клиентов, так же, как компании мобильных телефонов должны постоянно создавать новые модели.
Джорджио
2
Как я понял из SAMbdas в Java , одна из причин состоит в том, что было бы проблематично поддерживать обратную совместимость некоторых библиотек (коллекций).
Петр Пудлак
2
Связанный пост на SO, который ссылается на этот другой пост от Гетца .
assylias

Ответы:

6

Подход SAM на самом деле чем-то похож на то, что Scala (и C ++ 11) делают с анонимными функциями (созданными с помощью =>оператора Scala или []()синтаксиса C ++ 11 (лямбда)).

Первый вопрос, который нужно ответить на стороне Java, заключается в том, будет ли возвращаемый тип лямбда-оператора новым типом-примитивом, например intили byte, или каким-либо типом объекта. В Scala, там нет ни одного примитивного типа - даже целое число является объектом класса Int- и функция не отличается, являющиеся объектами класса Function1, Function2и так далее, в зависимости от количества аргументов функции принимает.

C ++ 11, ruby ​​и python также имеют лямбда-выражения, которые возвращают объект, который можно вызвать явным или неявным способом. Возвращенный объект имеет некоторый стандартный метод (например, #callкоторый можно использовать для вызова его как функции. Например, C ++ 11 использует std::functionтип, который перегружается, operator()так что вызовы метода вызова объекта даже выглядят как вызовы функций в текстовом виде. :-)

Когда новое предложение для Java становится беспорядочным, используется структурная типизация, позволяющая назначать такой метод другому объекту, например, Comparatorкоторый имеет единственный основной метод с другим именем . Хотя это концептуально неприглядное в некотором смысле, это делает означает , что полученный объект может быть передан в существующие функции , которые принимают объект для представления обратного вызова, компаратора, и так далее, и ожидать , чтобы быть в состоянии назвать вполне определенный сингл метод, такой как #compareили #callback. Уловка переопределения в C ++ operator()аккуратно уклонилась от этой проблемы еще до C ++ 11, поскольку все такие параметры обратного вызова можно было вызывать одинаково, и, следовательно, STL не требовал настройки, позволяющей использовать лямбда-выражения C ++ 11 сsortи так далее. Java, не использовавшая стандартного именования для таких объектов в прошлом (возможно, из-за того, что ни одна хитрость, такая как перегрузка операторов не делала очевидным единый подход), не столь удачна, так что этот хак не позволяет им менять целый ряд существующих API. ,

jimwise
источник
1
По иронии судьбы, проблему, если бы имелось множество различных несовместимых типов, представляющих какую-то «функцию, похожую на функцию», можно было бы аккуратно избежать, если на начальном этапе добавить стандартные типы функций в библиотеку, независимо от фактического буквального синтаксиса для их конструирования. Если бы был java.util.Function2<T1, T2, R>Java 1.0, тогда не было бы никакого Comparatorинтерфейса, вместо этого все эти методы взяли бы Function2<T1, T2, int>. (Ну, было бы целое множество интерфейсов, например, Function2TT_int<T1, T2>из-за примитивов, но вы понимаете мою точку зрения.)
Йорг Миттаг