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


какова мотивация для оценки назначения Scala в единицу, а не назначенное значение?

общий шаблон в программировании ввода-вывода заключается в следующем:

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

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

bytesRead = in.read(buffer)

.. возвращает единицу измерения, а не новое значение bytesRead.

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

8 78

8 ответов:

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

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

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

решение:

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

для чего бы это ни стоило.

EDIT

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

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

class MyBean {
  private var internalState: String = _

  def state = internalState

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 в более неявном виде без необходимости вводить его.