Pertanyaan Apa motivasi untuk penugasan Scala yang mengevaluasi ke Unit daripada nilai yang ditetapkan?


Apa motivasi untuk penugasan Scala yang mengevaluasi ke Unit daripada nilai yang ditetapkan?

Pola umum dalam pemrograman I / O adalah melakukan hal-hal seperti ini:

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

Tapi ini tidak mungkin di Scala karena ...

bytesRead = in.read(buffer)

.. mengembalikan Unit, bukan nilai baru byteRead.

Sepertinya hal yang menarik untuk meninggalkan bahasa fungsional. Saya bertanya-tanya mengapa hal itu dilakukan?


76
2018-01-04 10:37


asal


Jawaban:


Saya menganjurkan untuk memiliki tugas mengembalikan nilai yang diberikan daripada unit. Martin dan saya terus-menerus membahasnya, tetapi argumennya adalah bahwa meletakkan nilai pada tumpukan hanya untuk memunculkannya 95% dari waktu adalah pemborosan kode byte dan memiliki dampak negatif pada kinerja.


75
2018-01-04 16:18



Saya tidak mengetahui informasi di dalam mengenai alasan yang sebenarnya, tetapi kecurigaan saya sangat sederhana. Scala membuat loop yang memiliki efek samping canggung untuk digunakan sehingga pemrogram secara alami akan lebih memilih untuk pemahaman.

Ia melakukan ini dalam banyak cara. Misalnya, Anda tidak memiliki for loop di mana Anda menyatakan dan bermutasi suatu variabel. Anda tidak dapat (dengan mudah) mengubah keadaan pada while loop pada saat yang sama Anda menguji kondisi, yang berarti Anda sering harus mengulangi mutasi sebelum itu, dan pada akhirnya. Variabel yang dideklarasikan di dalam while blok tidak terlihat dari while kondisi pengujian, yang membuat do { ... } while (...) apalagi bermanfaat. Dan seterusnya.

Solusi:

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

Untuk apa pun itu layak.

Sebagai penjelasan alternatif, mungkin Martin Odersky harus menghadapi beberapa bug yang sangat buruk yang berasal dari penggunaan tersebut, dan memutuskan untuk melarangnya dari bahasanya.

EDIT

David Pollack punya dijawab dengan beberapa fakta aktual, yang jelas didukung oleh fakta itu Martin Odersky sendiri berkomentar jawabannya, memberikan kepercayaan pada argumen masalah yang terkait dengan kinerja yang diajukan oleh Pollack.


17
2018-01-04 11:57



Ini terjadi sebagai bagian dari Scala yang memiliki sistem tipe yang lebih "benar secara formal". Secara formal, penugasan adalah pernyataan yang benar-benar berpengaruh dan karenanya harus kembali Unit. Ini memang memiliki beberapa konsekuensi bagus; sebagai contoh:

class MyBean {
  private var internalState: String = _

  def state = internalState

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

Itu state_= metode kembali Unit (seperti yang diharapkan untuk setter) justru karena tugas kembali Unit.

Saya setuju bahwa untuk pola C-gaya seperti menyalin aliran atau serupa, keputusan desain khusus ini dapat sedikit merepotkan. Namun, itu sebenarnya relatif tidak bermasalah secara umum dan benar-benar memberikan kontribusi pada konsistensi keseluruhan dari sistem jenis.


9
2018-01-04 15:14



Mungkin ini disebabkan oleh pemisahan perintah-query prinsip?

CQS cenderung menjadi populer di persimpangan OO dan gaya pemrograman fungsional, karena CQS menciptakan perbedaan yang jelas antara metode objek yang melakukan atau tidak memiliki efek samping (yaitu, yang mengubah objek). Menerapkan CQS ke tugas variabel mengambil lebih jauh dari biasanya, tetapi ide yang sama berlaku.

Sebuah ilustrasi singkat mengapa CQS berguna: Pertimbangkan bahasa F / OO hibrida hipotetis dengan a List kelas yang memiliki metode Sort, Append, First, dan Length. Dalam gaya OO imperatif, seseorang mungkin ingin menulis fungsi seperti ini:

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

Sedangkan dalam gaya yang lebih fungsional, orang akan lebih mungkin menulis sesuatu seperti ini:

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

Sepertinya ini mencoba untuk melakukan hal yang sama, tetapi jelas salah satu dari keduanya tidak benar, dan tanpa mengetahui lebih banyak tentang perilaku metode, kita tidak bisa membedakan yang mana.

Menggunakan CQS, bagaimanapun, kami akan bersikeras bahwa jika Append dan Sort mengubah daftar, mereka harus mengembalikan tipe unit, sehingga mencegah kita membuat bug dengan menggunakan formulir kedua padahal seharusnya tidak. Kehadiran efek samping karena itu juga menjadi implisit dalam metode tanda tangan.


5
2018-01-04 16:38



Saya kira ini untuk menjaga program / bahasa bebas dari efek samping.

Apa yang Anda gambarkan adalah penggunaan yang disengaja dari efek samping yang dalam kasus umum dianggap sebagai hal yang buruk.


4
2018-01-04 10:42



Ini bukan gaya terbaik untuk menggunakan tugas sebagai ekspresi boolean. Anda melakukan dua hal pada saat yang sama yang sering menyebabkan kesalahan. Dan penggunaan akusatif "=" alih-alih "==" dihindari dengan pembatasan Scala.


4
2018-01-04 11:08



Ngomong-ngomong: Saya menemukan trik-trik awal yang bodoh, bahkan di Jawa. Kenapa tidak kadang-kadang seperti ini?

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

Memang, tugas itu muncul dua kali, tetapi setidaknya bytesRead berada dalam ruang lingkup miliknya, dan saya tidak bermain dengan trik penugasan lucu ...


2
2018-01-04 21:55



Anda dapat memiliki solusi untuk ini selama Anda memiliki tipe referensi untuk tipuan. Dalam penerapan yang naif, Anda dapat menggunakan yang berikut ini untuk jenis acak.

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

Kemudian, di bawah batasan yang harus Anda gunakan ref.value untuk mengakses referensi sesudahnya, Anda dapat menulis while predikat sebagai

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

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

dan Anda dapat melakukan pemeriksaan terhadap bytesRead dengan cara yang lebih implisit tanpa harus mengetiknya.


0
2018-06-18 23:03