Scala закрытие по сравнению с внутренними классами Java → final VS var

Я впервые задал этот вопрос об использовании final с анонимными внутренними классами в Java: Почему мы используем ключевое слово final с анонимными внутренними классами?

Я действительно читаю книгу Scala Мартина Одерского. Кажется, что Scala упрощает много Java-кода, но для закрытий Scala я мог заметить существенную разницу.

В то время как в Java мы "имитируем" замыкания с анонимным внутренним классом, фиксируя конечную переменную (которая будет скопирована, чтобы жить в куче вместо стека), кажется, что в Scala мы можем создать закрытие, которое может захватить val, но также и var, и, таким образом, обновить его в вызове закрытия!

Итак, мы можем использовать анонимный внутренний класс Java без ключевого слова final! Я не закончил читать книгу, но пока я не нашел достаточной информации об этом выборе дизайна языка.

Может ли кто-нибудь сказать мне, почему Мартин Одерски, который действительно заботится о функциональных побочных эффектах, выбирает замыкания, чтобы иметь возможность фиксировать как val, так и var вместо val?

Каковы преимущества и недостатки реализации Java и Scala?

Спасибо

Связанный вопрос: С закрытием Scala, когда захваченные переменные начинают жить в куче JVM?

Ответ 1

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

Кроме того, другие языки с изменяемыми переменными и анонимными функциями работают одинаково. Принцип лизинга. Java на самом деле WEIRD не позволяет захватить изменяемые переменные внутренними классами.

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

Downsides? Они имеют все недостатки общего изменчивого состояния.

Ответ 2

Вот некоторые преимущества и недостатки.

В дизайне языка существует принцип, согласно которому стоимость чего-то должна быть очевидна для программиста. (Я впервые увидел это в дизайне Холта и определении языка Тьюринга, но я забыл имя, которое он ему дал.) Две вещи, которые выглядят одинаково, должны стоить одинаково. Два локальных вара должны иметь схожие затраты. Это в пользу Java. Два локальных vars на Java реализованы одинаково и поэтому стоят одинаково независимо от того, упоминается ли один из них во внутреннем классе. Еще один момент в пользу Java заключается в том, что в большинстве случаев захваченная переменная действительно является окончательной, поэтому для программиста мало затрат на сбор нефинальных локальных варов. Также настойчивость в окончательном упрощении компилятора, так как это означает, что все локальные переменные являются переменными стека.

Существует еще один принцип дизайна языка, который говорит, что он ортогонален. Если val может быть захвачен, почему не var. Если есть разумная интерпретация, зачем вводить ограничения. Когда языки недостаточно ортогональны, они кажутся извращенными. С другой стороны, языки с слишком большой ортогональностью могут иметь сложные (и, следовательно, ошибочные и/или поздние и/или несколько) реализации. Например, Algol 68 имел ортогональность в пиках, но реализовать его было непросто, что означало мало реализаций и малое восприятие. Паскаль (спроектированный примерно в то же время) имел всевозможные неэлегантные ограничения, которые упрощали составление письменных компиляторов. Результатом было множество реализаций и много понимания.