Закрытия в Scala против Закрытий в Java - PullRequest
33 голосов
/ 26 мая 2011

Некоторое время назад Oracle решила, что было бы неплохо добавить Closures в Java 8. Интересно, как там решаются проблемы с дизайном по сравнению со Scala, которая закрывалась с первого дня.

Ссылаясь на Открытые выпуски от javac.info :

  1. Можно ли использовать дескрипторы метода для типов функций? Не очевидно, как заставить это работать. Одна из проблем заключается в том, что дескрипторы метода изменяют параметры типа, но таким образом, что это мешает подтипу функции.

  2. Можем ли мы избавиться от явного объявления параметров типа throws? Идея состоит в том, чтобы использовать дизъюнктивный вывод типа всякий раз, когда объявленная граница является проверенным типом исключения. Это не совсем обратно совместимо, но вряд ли нарушит реально существующий код. Однако мы, вероятно, не можем избавиться от «бросков» в аргументе типа из-за синтаксической неоднозначности.

  3. Запретить @Shared для переменных индекса цикла старого стиля

  4. Обрабатывать интерфейсы, такие как Comparator, которые определяют более одного метода, все, кроме одного, будут реализованы методом, унаследованным от Object. Определение «интерфейс с одним методом» должно учитывать только те методы, которые не были бы реализованы методом в Object, и должно учитывать несколько методов как один, если реализация одного из них будет реализовывать их все. Главным образом, для этого требуется более точная спецификация того, что значит для интерфейса иметь только один абстрактный метод.

  5. Укажите отображение типов функций на интерфейсы: имена, параметры и т. Д. Мы должны полностью определить соответствие между типами функций и сгенерированными системой интерфейсами точно.

  6. Вывод типа. Необходимо дополнить правила вывода типа, чтобы учесть вывод параметров типа исключения. Точно так же должны быть отражены отношения подтипа, используемые преобразованием замыкания.

  7. Параметры типа исключенных исключений, помогающие модифицировать прозрачность исключений. Возможно, сделать параметры типа исключенного исключения означают ограничение. Это позволяет модифицировать существующие универсальные интерфейсы, которые не имеют параметра типа для исключения, например java.util.concurrent.Callable, путем добавления нового универсального параметра исключения.

  8. Как формируются литералы классов для типов функций? Это #void (). Class? Если да, то как это работает, если типы объектов стираются? Это #? (?). Класс?

  9. Загрузчик системного класса должен динамически генерировать интерфейсы типов функций. Интерфейсы, соответствующие типам функций, должны генерироваться по требованию загрузчиком класса начальной загрузки, чтобы они могли быть общими для всего пользовательского кода. Для прототипа мы могли бы с помощью javac сгенерировать эти интерфейсы, чтобы сгенерированный прототипом код мог работать на стандартных (JDK5-6) виртуальных машинах.

  10. Должна ли оценка лямбда-выражения каждый раз производить свежий объект? Надеюсь, что нет. Например, если лямбда не захватывает переменные из окружающей области, она может быть размещена статически. Точно так же в других ситуациях лямбда может быть перемещена из внутреннего цикла, если она не захватывает переменные, объявленные в цикле. Поэтому было бы лучше, если бы спецификация ничего не обещала относительно ссылочной идентичности результата лямбда-выражения, поэтому такие оптимизации могут быть выполнены компилятором.

Насколько я понимаю, 2, 6 и 7 не являются проблемой в Scala, потому что Scala не использует Checked Exceptions в качестве некой «системы типов теней», такой как Java.

А как насчет отдыха?

Ответы [ 2 ]

29 голосов
/ 26 мая 2011

1) Можно ли использовать дескрипторы метода для типов функций?

Scala нацеливается на JDK 5 и 6, у которых нет дескрипторов метода, поэтому он не пытался с этим справитьсяпока нет.

2) Можем ли мы избавиться от явного объявления параметров типа throws?

В Scala нет проверенных исключений.

3) Запретить @Shared для переменных индекса цикла старого стиля.

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

4) Обрабатывать интерфейсы, такие как Comparator, которые определяют более одного метода, все из которых, кроме одного,происходят из Object

Пользователи Scala склонны использовать функции (или неявные функции) для приведения функций правильного типа к интерфейсу.Например,

[implicit] def toComparator[A](f : (A, A) => Int) = new Comparator[A] { 
    def compare(x : A, y : A) = f(x, y) 
}

5) Укажите отображение типов функций на интерфейсы:

Стандартная библиотека Scala включает в себя черты FuncitonN для 0 <= N <= 22, и в спецификации говоритсяэти литералы функций создают экземпляры этих признаков </p>

6) Вывод типа.Правила для вывода типов необходимо дополнить, чтобы учесть вывод параметров типа исключений.

Поскольку у Scala нет проверенных исключений, она может решить всю эту проблему

7) Параметры типа исключенных исключений, помогающие модифицировать прозрачность исключений.

Та же сделка, без проверенных исключений.

8) Как формируются литералы классов для типов функций?Это #void (). Class?Если да, то как это работает, если типы объектов стираются?Это #? (?). Класс?

classOf[A => B] //or, equivalently, 
classOf[Function1[A,B]]

Стирание типа - это стирание типа.Вышеупомянутые литералы создают scala.lang.Function1 независимо от выбора для A и B. Если вы предпочитаете, вы можете написать

classOf[ _ => _ ] // or
classOf[Function1[ _,_ ]]

9) Загрузчик системного класса должен динамически генерировать интерфейсы типов функций.

Scala произвольно ограничивает число аргументов не более 22, чтобы не создавать динамически классы FunctionN.

10) Необходимо выполнить оценкулямбда-выражение каждый раз производит свежий объект?

Спецификация Scala не говорит, что это необходимо.Но, начиная с версии 2.8.1, компилятор не оптимизирует случай, когда лямбда-выражение ничего не захватывает из своего окружения.Я еще не тестировал с 2.9.0.

12 голосов
/ 27 мая 2011

Здесь я расскажу только номер 4.

Одна из вещей, которая отличает "замыкания" Java от замыканий, встречающихся в других языках, заключается в том, что они могут использоваться вместо интерфейса, который не описывает функцию- например, Runnable.Это то, что подразумевается под SAM, Single Abstract Method.

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

Например, Arrays.sort принимает объект Comparator, который будет выполнять сравнение элементов сортируемого массива.В отличие от этого, Scala может отсортировать List[A], получив функцию (A, A) => Int, которая легко проходит через замыкание.Тем не менее, обратите внимание на примечание 1.

Итак, поскольку библиотека Scala была создана для языка с типами функций и замыканиями, нет необходимости поддерживать такую ​​вещь, как замыкания SAM в Scala.

Конечно, существует вопрос о совместимости Scala / Java - хотя библиотеке Scala может не понадобиться что-то вроде SAM, библиотека Java делает это.Есть два пути, которые можно решить.Во-первых, поскольку Scala поддерживает замыкания и типы функций, очень легко создавать вспомогательные методы.Например:

def runnable(f: () => Unit) = new Runnable {
    def run() = f()
}

runnable { () => println("Hello") } // creates a Runnable

На самом деле этот конкретный пример можно сделать еще короче, если использовать параметры по имени Scala, но это не относится к делу.Во всяком случае, это то, что, возможно, Java мог бы сделать вместо того, что он собирается делать.Учитывая преобладание интерфейсов SAM, это не так уж удивительно.

Другой способ, которым Scala справляется с этим, - неявные преобразования.Просто добавив implicit к методу runnable, приведенному выше, можно создать метод, который автоматически применяется (примечание 2), когда требуется Runnable, но предоставляется функция () => Unit.

Последствияоднако, очень уникальный и все еще спорный в некоторой степени.

Примечание 1 : На самом деле, этот конкретный пример был выбран с некоторой злобой ... Comparator имеет два абстрактные методы вместо одного, в этом вся проблема.Поскольку один из его методов может быть реализован в терминах другого, я думаю, они просто «вычтут» методы защитника из абстрактного списка.

И, на стороне Scala, даже если есть метод сортировки, которыйиспользует (A, A) => Boolean, а не (A, A) => Int, стандартный метод сортировки вызывает объект Ordering, который очень похож на Comparator в Java!Однако в случае Scala Ordering выполняет роль класса класса .

Примечание 2 : последствия автоматически применяются после того, как они былиимпортировано в сферу .

...