Какова мотивация для оценки присвоения Scala к Единице, а не присвоенному значению?

Какова мотивация для оценки присвоения Scala к Единице, а не присвоенному значению?

Общий шаблон в программировании ввода-вывода должен сделать вещи как это:

while ((bytesRead = in.read(buffer)) != -1) { ...

Но это не возможно в Scala потому что...

bytesRead = in.read(buffer)

.. Единица возвратов, не новое значение bytesRead.

Походит на интересную вещь упустить из функционального языка. Я задаюсь вопросом, почему это было сделано так?

82
задан Graham Lea 4 January 2010 в 10:37
поделиться

8 ответов

Я выступал за то, чтобы назначения возвращали присвоенное значение, а не единицу измерения. Мы с Мартином ходили по нему туда-сюда, но его аргумент состоял в том, что помещение значения в стек только для того, чтобы вытащить его из 95% времени, является пустой тратой байт-кодов и отрицательно влияет на производительность

.
81
ответ дан 24 November 2019 в 09:18
поделиться

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

class MyBean {
  private var internalState: String = _

  def state = internalState

  def state_=(state: String) = internalState = state
}

Метод state_= возвращает Unit (как и следовало ожидать от сеттера) именно потому, что присваивание возвращает Unit.

Я согласен, что для паттернов С-стиля, таких как копирование потока или подобных, это конкретное конструктивное решение может быть немного проблематичным. Однако, на самом деле оно относительно беспроблемно в целом и действительно способствует общей согласованности системы типов.

.
11
ответ дан 24 November 2019 в 09:18
поделиться

Полагаю, это для того, чтобы сохранить программу/язык свободным от побочных эффектов.

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

.
4
ответ дан 24 November 2019 в 09:18
поделиться

Это не лучший стиль использования задания в качестве булевого выражения. Вы выполняете одновременно две вещи, что часто приводит к ошибкам. И случайного использования "=" вместо "==" можно избежать с помощью ограничения Скаласа.

.
4
ответ дан 24 November 2019 в 09:18
поделиться

Кстати: Я нахожу начальное время-трик глупым, даже на Java. Почему бы и нет?

for(int bytesRead = in.read(buffer); bytesRead != -1; bytesRead = in.read(buffer)) {
   //do something 
}

Разрешено, присваивание появляется дважды, но, по крайней мере, байтRead находится в области видимости, к которой оно принадлежит, и я не играю с забавными трюками присваивания...

.
2
ответ дан 24 November 2019 в 09:18
поделиться

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

Он делает это разными способами. Например, у вас нет цикла for , в котором вы объявляете и изменяете переменную. Вы не можете (легко) изменить состояние в цикле while одновременно с проверкой условия, что означает, что вам часто приходится повторять мутацию непосредственно перед ним и в конце. Переменные, объявленные внутри блока while , не видны из условия проверки while , что делает do {...} while (...) гораздо менее полезным . И так далее.

Решение:

while ({bytesRead = in.read(buffer); bytesRead != -1}) { ... 

Независимо от того, что стоит.

В качестве альтернативного объяснения, возможно, Мартин Одерский столкнулся с несколькими очень уродливыми ошибками, возникающими из-за такого использования, и решил объявить это вне закона в своем языке.

РЕДАКТИРОВАТЬ

Дэвид Поллак ответил некоторыми фактическими фактами, которые явно подтверждаются тем фактом, что сам Мартин Одерский прокомментировал свой ответ, поверив аргумент о проблемах, связанных с производительностью, выдвинутый Поллаком.

20
ответ дан 24 November 2019 в 09:18
поделиться

Возможно, это связано с принципом разделения команд и запросов ?

CQS имеет тенденцию быть популярным на пересечении объектно-ориентированного и функционального стилей программирования, поскольку он создает очевидное различие между методами объекта, которые имеют или не имеют побочных эффектов (т. е. изменяют объект). Применение CQS к присвоению переменных идет дальше обычного, но применима та же идея.

Краткая иллюстрация того, почему CQS полезен: рассмотрим гипотетический гибридный язык F / OO с классом List , который имеет методы Sort , Append , Первый и Длина . В императивном объектно-ориентированном стиле можно написать такую ​​функцию:

func foo(x):
    var list = new List(4, -2, 3, 1)
    list.Append(x)
    list.Sort()
    # list now holds a sorted, five-element list
    var smallest = list.First()
    return smallest + list.Length()

В то время как в более функциональном стиле можно было бы написать что-то вроде этого:

func bar(x):
    var list = new List(4, -2, 3, 1)
    var smallest = list.Append(x).Sort().First()
    # list still holds an unsorted, four-element list
    return smallest + list.Length()

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

Однако, используя CQS, мы настаиваем на том, что если Append и Sort изменят список, они должны возвращать тип объекта, таким образом предотвращая создание ошибок при использовании второй формы. когда мы не должны. Таким образом, наличие побочных эффектов также становится неявным в сигнатуре метода.

7
ответ дан 24 November 2019 в 09:18
поделиться

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

case class Ref[T](var value: T) {
  def := (newval: => T)(pred: T => Boolean): Boolean = {
    this.value = newval
    pred(this.value)
  }
}

Затем, при ограничении, что вам придется использовать ref.value для последующего доступа к ссылке, вы можете написать свой while предикат как

val bytesRead = Ref(0) // maybe there is a way to get rid of this line

while ((bytesRead := in.read(buffer)) (_ != -1)) { // ...
  println(bytesRead.value)
}

и вы можете сделать проверку на bytesRead более неявным образом без необходимости вводить его.

0
ответ дан 24 November 2019 в 09:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: