Какова мотивация для оценки присвоения Scala единице, а не назначенной стоимости?
какова мотивация для оценки назначения Scala в единицу, а не назначенное значение?
общий шаблон в программировании ввода-вывода заключается в следующем:
while ((bytesRead = in.read(buffer)) != -1) { ...
но это невозможно в Scala, потому что...
bytesRead = in.read(buffer)
.. возвращает единицу измерения, а не новое значение bytesRead.
кажется интересной вещью, чтобы оставить вне функционального языка. Мне интересно, почему это было сделано так?
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в более неявном виде без необходимости вводить его.