Подтвердить что ты не робот

Закрытие в Scala vs Closures в Java

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

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

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

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

  • Запретить @Shared по переменным индекса цикла в старом стиле

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

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

  • Вывод типа. Правила для вывода типа должны быть дополнены для размещения вывода параметров типа исключения. Аналогично, отношения подтипов, используемые преобразованием замыкания, также должны быть отражены.

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

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

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

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

Насколько я понимаю 2., 6. и 7. не проблема в Scala, потому что Scala не использует Checked Exceptions как своего рода "Shadow type-system", например Java.

Как насчет остальных?

4b9b3361

Ответ 1

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, а спецификация говорит, что литералы функций создают экземпляры этих признаков

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

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

7) Определены параметры типа исключения, чтобы помочь изменить прозрачность исключений.

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

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

Ответ 2

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

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

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

Например, Arrays.sort принимает объект Comparator, который будет выполнять сравнение между членами массива, подлежащего сортировке. Напротив, Scala может сортировать a 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 by-name, но это рядом с точкой. Во всяком случае, это то, что, возможно, могло бы сделать Java, а не то, что он собирается делать. Учитывая преобладание интерфейсов SAM, это не удивительно.

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

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

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

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

Примечание 2: Implicits автоматически применяются после их импорта в область.