Jaka jest motywacja przypisania Scali do jednostki, a nie przypisanej wartości?

Jaka jest motywacja przypisania Scali do jednostki, a nie przypisanej wartości?

Powszechnym wzorcem w programowaniu We / Wy jest wykonywanie takich rzeczy:

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

Ale nie jest to możliwe w Scali, ponieważ...

bytesRead = in.read(buffer)

.. zwraca jednostkę, a nie nową wartość bytesRead.

[[2]}wydaje się być interesującą rzeczą, aby pominąć język funkcjonalny. Zastanawiam się, dlaczego tak się stało?
Author: Graham Lea, 2010-01-04

8 answers

Zalecałem, aby przypisania zwracały wartość przypisaną, a nie jednostkę. Martin i ja poszliśmy tam iz powrotem na to, ale jego argumentem było to, że umieszczenie wartości na stosie tylko po to, aby go wyłączyć 95% czasu było stratą kodów bajtowych i mieć negatywny wpływ na wydajność.

 77
Author: David Pollak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-04 16:18:56

Nie jestem wtajemniczony w wewnętrzne informacje o rzeczywistych przyczynach, ale moje podejrzenia są bardzo proste. Scala sprawia, że pętle poboczne są niewygodne w użyciu, dzięki czemu programiści będą naturalnie preferować składanie.

Robi to na wiele sposobów. Na przykład, nie masz pętli for, w której deklarujesz i mutujesz zmienną. Nie można (łatwo) mutować stanu na pętli while w tym samym czasie testujesz warunek, co oznacza, że często musisz powtórzyć mutację tuż przed nią, a na koniec. Zmienne zadeklarowane wewnątrz bloku while nie są widoczne z warunku testowego while, co czyni do { ... } while (...) znacznie mniej użytecznymi. I tak dalej.

Obejście:

while ({bytesRead = in.read(buffer); bytesRead != -1}) { ... 
Cokolwiek to jest warte. Jako alternatywne Wyjaśnienie, być może Martin Odersky musiał zmierzyć się z kilkoma bardzo brzydkimi błędami wynikającymi z takiego użycia i postanowił zakazać tego ze swojego języka.

EDIT

David Pollack odpowiedział pewnymi faktami, które w 2007 roku, po raz pierwszy w historii, Martin Odersky skomentował swoją odpowiedź, dając wiarę w kwestie związane z występem, które przedstawił Pollack.

 18
Author: Daniel C. Sobral,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:33:13

[[5]}stało się to jako część Scali posiadającej bardziej "poprawny formalnie" system typów. Formalnie rzecz biorąc, przypisanie jest deklaracją czysto uboczną i dlatego powinno zwracać Unit. To ma jakieś miłe konsekwencje; na przykład:

class MyBean {
  private var internalState: String = _

  def state = internalState

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

Metoda state_= zwraca Unit (zgodnie z oczekiwaniami settera) właśnie dlatego, że przypisanie zwraca Unit.

Zgadzam się, że dla wzorców w stylu C, takich jak kopiowanie strumienia lub podobnych, ta konkretna decyzja projektowa może być trochę kłopotliwe. Jednak w rzeczywistości jest to stosunkowo bezproblemowe w ogóle i naprawdę przyczynia się do ogólnej spójności systemu typów.

 9
Author: Daniel Spiewak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-04 15:14:46

Być może wynika to z zasady separacji komend i zapytań?

CQS jest popularne na przecięciu OO i stylów programowania funkcyjnego, ponieważ tworzy oczywiste rozróżnienie między metodami obiektowymi, które mają lub nie mają efektów ubocznych (tj. które zmieniają obiekt). Zastosowanie CQS do przyporządkowania zmiennych idzie dalej niż zwykle, ale dotyczy to tego samego pomysłu.

Krótka ilustracja, dlaczego CQS jest przydatny: rozważmy hipotetyczną hybrydę F / OO język z klasą List, która ma metody Sort, Append, First, i Length. W imperatywnym stylu OO można by napisać taką funkcję:

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()

Podczas gdy w bardziej funkcjonalnym stylu, bardziej prawdopodobne byłoby napisanie czegoś takiego:

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()

Wydają się być próbą zrobienia tego samego, ale oczywiście jedna z nich jest niepoprawna i nie wiedząc więcej o zachowaniu metod, nie możemy stwierdzić, która z nich.

Używając CQS, jednak, będziemy nalegać, że jeśli Append i Sort zmienią listę, muszą zwrócić typ jednostki, co uniemożliwia nam tworzenie błędów przy użyciu drugiej formy, gdy nie powinniśmy. obecność efektów ubocznych staje się zatem również ukryta w sygnaturze metody.

 5
Author: C. A. McCann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-04 16:38:11

Zgaduję, że jest to po to, aby program / język był wolny od skutków ubocznych.

To, co opisujesz, to celowe użycie efektu ubocznego, który w ogólnym przypadku jest uważany za złą rzecz.

 4
Author: Jens Schauder,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-04 10:42:49

Użycie przypisania jako wyrażenia logicznego nie jest najlepszym stylem. Wykonujesz dwie rzeczy jednocześnie, co często prowadzi do błędów. Przy ograniczeniu skalowania unika się przypadkowego użycia "=" zamiast"==".

 4
Author: deamon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-04 11:41:06

Przy okazji: uważam, że inicjał while-trick jest głupi, nawet w Javie. Dlaczego nie coś takiego?

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

Przyznane, przypisanie pojawia się dwa razy, ale przynajmniej bytesRead jest w zakresie, do którego należy, i nie bawię się zabawnymi sztuczkami przypisania...

 2
Author: Landei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-04 21:55:21

Możesz mieć obejście tego problemu, o ile masz typ odniesienia dla indrection. W naiwnej implementacji, możesz użyć następujących dla dowolnych typów.

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

Następnie, pod warunkiem, że będziesz musiał użyć ref.value, aby później uzyskać dostęp do referencji, możesz zapisać swój predykat while jako

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

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

I możesz zrobić sprawdzenie przeciwko bytesRead w bardziej ukryty sposób bez konieczności wpisywania go.

 0
Author: Debilski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-18 23:03:17