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