Saya mencoba mewakili fungsi yang tidak menggunakan argumen dan tidak mengembalikan nilai (Saya mensimulasikan fungsi setTimeout dalam JavaScript, jika Anda harus tahu.)
case class Scheduled(time : Int, callback : => Unit)
tidak dikompilasi, mengatakan "parameter` val 'mungkin bukan panggilan-dengan-nama "
case class Scheduled(time : Int, callback : () => Unit)
kompilasi, tetapi harus dipanggil aneh, bukan
Scheduled(40, { println("x") } )
Aku harus melakukan ini
Scheduled(40, { () => println("x") } )
Yang juga berhasil adalah
class Scheduled(time : Int, callback : Unit => Unit)
tetapi dipanggil dengan cara yang bahkan kurang masuk akal
Scheduled(40, { x : Unit => println("x") } )
(Apa yang akan menjadi variabel tipe Unit?) Apa yang saya inginkan tentu saja adalah konstruktor yang dapat memanggil cara saya akan memintanya jika itu adalah fungsi biasa:
Scheduled(40, println("x") )
Berikan bayinya botol!
case class Scheduled(time: Int)(callback: => Unit)
. Ini berfungsi karena daftar parameter sekunder tidak diekspos secara publik, juga tidak termasuk dalam metodeequals
/ yang dihasilkanhashCode
.Jawaban:
Call-by-Name: => Ketik
The
=> Type
notasi singkatan call-by-nama, yang merupakan salah satu dari banyak cara parameter dapat dilewatkan. Jika Anda tidak terbiasa dengan mereka, saya sarankan meluangkan waktu untuk membaca artikel wikipedia itu, meskipun saat ini sebagian besar panggilan-oleh-nilai dan panggilan-oleh-referensi.Apa artinya adalah bahwa apa yang dilewatkan diganti dengan nama nilai di dalam fungsi. Misalnya, ambil fungsi ini:
Jika saya menyebutnya seperti ini
Maka kode akan dieksekusi seperti ini
Meskipun itu memunculkan poin tentang apa yang terjadi jika ada bentrokan nama pengidentifikasi. Dalam panggilan-dengan-nama tradisional, suatu mekanisme yang disebut subtitusi penghindaran terjadi untuk menghindari perselisihan nama. Di Scala, bagaimanapun, ini diimplementasikan dengan cara lain dengan hasil yang sama - nama pengidentifikasi di dalam parameter tidak dapat merujuk atau pengidentifikasi bayangan dalam fungsi yang dipanggil.
Ada beberapa hal lain yang terkait dengan panggilan-nama-yang akan saya bicarakan setelah menjelaskan dua lainnya.
0-arity Fungsi: () => Ketik
Sintaksnya
() => Type
adalah tipe aFunction0
. Yaitu, fungsi yang tidak mengambil parameter dan mengembalikan sesuatu. Ini sama dengan, katakanlah, memanggil metodesize()
- tidak memerlukan parameter dan mengembalikan nomor.Sangat menarik, bagaimanapun, bahwa sintaks ini sangat mirip dengan sintaks untuk fungsi literal anonim , yang merupakan penyebab kebingungan. Sebagai contoh,
adalah fungsi anonim literal dari arity 0, yang tipenya adalah
Jadi kita bisa menulis:
Namun, penting untuk tidak membingungkan tipe dengan nilainya.
Unit => Jenis
Ini sebenarnya hanya sebuah
Function1
, yang parameter pertamanya bertipeUnit
. Cara lain untuk menulisnya adalah(Unit) => Type
atauFunction1[Unit, Type]
. Masalahnya adalah ... ini tidak mungkin menjadi apa yang diinginkan seseorang. TheUnit
Tujuan utama tipe ini mengindikasikan nilai salah satu tidak tertarik, jadi tidak masuk akal untuk menerima nilai tersebut.Pertimbangkan, misalnya,
Apa yang bisa dilakukan seseorang
x
? Ini hanya dapat memiliki nilai tunggal, jadi orang tidak perlu menerimanya. Satu kemungkinan penggunaan adalah fungsi chaining kembaliUnit
:Karena
andThen
hanya didefinisikanFunction1
, dan fungsi-fungsi yang kita gunakan kembaliUnit
, kita harus mendefinisikannya sebagai tipeFunction1[Unit, Unit]
untuk dapat mengaitkannya.Sumber Kebingungan
Sumber pertama kebingungan adalah memikirkan kesamaan antara tipe dan literal yang ada untuk fungsi 0-arity juga ada untuk panggilan-dengan-nama. Dengan kata lain, memikirkan itu, karena
adalah untuk harfiah
() => Unit
, makaakan menjadi literal untuk
=> Unit
. Bukan itu. Itu adalah blok kode , bukan literal.Sumber kebingungan lainnya adalah bahwa nilai
Unit
type ditulis , yang terlihat seperti daftar parameter 0-arity (tetapi tidak).()
sumber
case ... =>
, jadi saya tidak menyebutkannya. Sedih tapi benar. :-)1
, karakter'a'
, string"abc"
, atau fungsi() => println("here")
, untuk beberapa contoh). Itu bisa dilewatkan sebagai argumen, disimpan dalam variabel, dll. "Blok kode" adalah pembatas statik sintaksis - ini bukan nilai, tidak bisa diedarkan, atau semacamnya.(Unit) => Type
vs() => Type
- yang pertama adalahFunction1[Unit, Type]
, sedangkan yang kedua adalah aFunction0[Type]
.The
case
pengubah membuat implisitval
dari setiap argumen ke konstruktor. Oleh karena itu (seperti yang dicatat seseorang) jika Anda menghapuscase
Anda dapat menggunakan parameter panggilan-dengan-nama. Kompiler mungkin bisa mengizinkannya, tetapi mungkin akan mengejutkan orang-orang jika itu dibuatval callback
alih-alih diubah menjadilazy val callback
.Ketika Anda mengubah ke
callback: () => Unit
sekarang kasing Anda hanya mengambil fungsi alih-alih parameter panggilan-dengan-nama. Jelas fungsi tersebut dapat disimpanval callback
sehingga tidak ada masalah.Cara termudah untuk mendapatkan apa yang Anda inginkan (di
Scheduled(40, println("x") )
mana parameter panggilan-dengan-nama digunakan untuk melewati lambda) mungkin untuk melewatkancase
dan secara eksplisit membuatapply
yang Anda tidak bisa dapatkan di tempat pertama:Digunakan:
sumber
Dalam pertanyaan, Anda ingin mensimulasikan fungsi SetTimeOut dalam JavaScript. Berdasarkan jawaban sebelumnya, saya menulis kode berikut:
Di REPL, kita bisa mendapatkan sesuatu seperti ini:
Simulasi kami tidak berperilaku sama persis dengan SetTimeOut, karena simulasi kami adalah fungsi pemblokiran, tetapi SetTimeOut adalah non-pemblokiran.
sumber
Saya melakukannya dengan cara ini (hanya tidak ingin melanggar berlaku):
dan menyebutnya
sumber