Saya dapat melihat di dokumen API untuk Predef bahwa mereka adalah subkelas dari tipe fungsi umum (Dari) => Ke, tetapi hanya itu yang dikatakan. Um, apa? Mungkin ada dokumentasi di suatu tempat, tetapi mesin pencari tidak menangani "nama" seperti "<: <" sangat baik, jadi saya belum dapat menemukannya.
Pertanyaan tindak lanjut: kapan saya harus menggunakan simbol / kelas yang funky ini, dan mengapa?
typeclass
melakukan pekerjaan operator ini? Contoh:compare :: Ord a => a -> a -> Ordering
? Saya mencoba untuk memahami konsep Scala ini sehubungan dengan mitra Haskell-nya.Jawaban:
Ini disebut batasan tipe umum . Mereka memungkinkan Anda, dari dalam kelas tipe-parameter atau sifat, untuk lebih jauh membatasi salah satu parameter jenisnya. Ini sebuah contoh:
Argumen implisit
evidence
disediakan oleh kompiler, iffA
adalahString
. Anda dapat menganggap itu sebagai bukti bahwaA
adalahString
argumen --the itu sendiri tidak penting, hanya mengetahui bahwa itu ada. [edit: baiklah, secara teknis itu sebenarnya penting karena itu merupakan konversi implisit dariA
keString
, yang memungkinkan Anda menelepona.length
dan tidak meminta kompiler berteriak kepada Anda]Sekarang saya bisa menggunakannya seperti ini:
Tetapi jika saya mencoba menggunakannya dengan
Foo
sesuatu yang mengandung selainString
:Anda dapat membaca kesalahan itu sebagai "tidak dapat menemukan bukti bahwa Int == String" ... itu sudah seharusnya!
getStringLength
memaksakan pembatasan lebih lanjut pada jenis dariA
apa yangFoo
secara umum mengharuskan; yaitu, Anda hanya dapat memintagetStringLength
pada aFoo[String]
. Batasan ini diberlakukan pada waktu kompilasi, yang keren!<:<
dan<%<
bekerja serupa, tetapi dengan sedikit variasi:A =:= B
berarti A harus persis BA <:< B
berarti A harus merupakan subtipe B (analog dengan batasan tipe sederhana<:
)A <%< B
berarti A harus dapat dilihat sebagai B, mungkin melalui konversi implisit (analog dengan batasan tipe sederhana<%
)Cuplikan oleh @retronym ini adalah penjelasan yang bagus tentang bagaimana hal seperti ini dapat diselesaikan dan bagaimana batasan tipe umum membuatnya lebih mudah sekarang.
TAMBAHAN
Untuk menjawab pertanyaan tindak lanjut Anda, diakui contoh yang saya berikan cukup dibuat-buat dan tidak jelas berguna. Tapi bayangkan menggunakannya untuk mendefinisikan sesuatu seperti
List.sumInts
metode, yang menambahkan daftar bilangan bulat. Anda tidak ingin mengizinkan metode ini dipanggil pada yang lamaList
, hanya aList[Int]
. NamunList
konstruktor tipe tidak dapat dibatasi; Anda masih ingin dapat memiliki daftar string, foo, bar, dan apa pun. Jadi dengan menempatkan batasan tipe umumsumInts
, Anda dapat memastikan bahwa hanya metode yang memiliki kendala tambahan yang hanya dapat digunakan pada aList[Int]
. Pada dasarnya Anda sedang menulis kode kasus khusus untuk beberapa jenis daftar.sumber
Manifest
, yang tidak Anda sebutkan.Manifest
yang<:<
dan>:>
hanya ... sejak OP disebutkan persis 3 varietas jenis kendala umum, saya mengasumsikan bahwa apa yang dia tertarik.class =:=[From, To] extends From => To
, yang berarti bahwa nilai tipeFrom =:= To
implisit sebenarnya merupakan konversi implisit dariFrom
menjadiTo
. Jadi dengan menerima parameter implisit dari tipe yangA =:= String
Anda katakan yangA
dapat secara implisit dikonversiString
. Jika Anda mengubah urutan dan membuat argumen implisit menjadi tipeString =:= A
, itu tidak akan berhasil, karena ini akan menjadi konversi implisit dariString
menjadiA
.From =:= To
lingkup menyiratkan bahwa Anda memiliki konversi implisitFrom => To
, tetapi implikasinya tidak berjalan mundur; memiliki konversi implisitA => B
tidak tidak berarti Anda memiliki sebuah instance dariA =:= B
.=:=
adalah kelas abstrak tersegel yang didefinisikan dalamscala.Predef
, dan hanya memiliki satu contoh terbuka untuk umum, yang tersirat, dan bertipeA =:= A
. Jadi, Anda dijamin bahwa nilai implisit tipeA =:= B
saksi faktaA
danB
sama.Bukan jawaban yang lengkap (orang lain sudah menjawab ini), saya hanya ingin mencatat yang berikut, yang mungkin membantu untuk memahami sintaks lebih baik: Cara Anda biasanya menggunakan "operator" ini, seperti misalnya dalam contoh pelotom:
memanfaatkan alternatif Scala sintaks infix untuk operator tipe .
Jadi,
A =:= String
sama dengan=:=[A, String]
(dan=:=
hanya kelas atau sifat dengan nama yang tampak mewah). Perhatikan bahwa sintaks ini juga berfungsi dengan kelas "reguler", misalnya Anda dapat menulis:seperti ini:
Ini mirip dengan dua sintaks untuk panggilan metode, "normal" dengan
.
dan()
dan sintaksis operator.sumber
makes use of Scala's alternative infix syntax for type operators.
sama sekali kehilangan penjelasan ini yang tanpanya semuanya tidak masuk akalBaca jawaban lain untuk memahami apa itu konstruksi. Inilah saatnya Anda harus menggunakannya. Anda menggunakannya ketika Anda perlu membatasi metode untuk tipe tertentu saja.
Berikut ini sebuah contoh. Misalkan Anda ingin mendefinisikan Pasangan yang homogen, seperti ini:
Sekarang Anda ingin menambahkan metode
smaller
, seperti ini:Itu hanya berfungsi jika
T
dipesan. Anda dapat membatasi seluruh kelas:Tapi itu memalukan - mungkin ada manfaat untuk kelas saat
T
tidak dipesan. Dengan batasan tipe, Anda masih bisa mendefinisikansmaller
metode:Tidak masalah untuk instantiate, katakanlah, a
Pair[File]
, selama Anda tidak meneleponsmaller
.Dalam kasus ini
Option
, para pelaksana menginginkan suatuorNull
metode, meskipun itu tidak masuk akalOption[Int]
. Dengan menggunakan batasan tipe, semuanya baik-baik saja. Anda dapat menggunakanorNull
padaOption[String]
, dan Anda dapat membentukOption[Int]
dan menggunakannya, selama Anda tidak memanggilnyaorNull
. Jika Anda mencobaSome(42).orNull
, Anda mendapatkan pesan yang menariksumber
<:<
, dan saya pikirOrdered
contohnya tidak begitu menarik lagi karena sekarang Anda lebih suka menggunakanOrdering
typeclass daripadaOrdered
sifat. Sesuatu seperti:def smaller(implicit ord: Ordering[T]) = if (ord.lt(first, second)) first else second
.Itu tergantung di mana mereka digunakan. Paling sering, ketika digunakan saat mendeklarasikan tipe parameter implisit, mereka adalah kelas. Mereka juga bisa menjadi objek dalam kasus yang jarang terjadi. Akhirnya, mereka bisa menjadi operator pada
Manifest
objek. Mereka didefinisikan di dalamscala.Predef
dalam dalam dua kasus pertama, meskipun tidak didokumentasikan dengan baik.Mereka dimaksudkan untuk menyediakan cara untuk menguji hubungan antara kelas, seperti
<:
dan<%
lakukan, dalam situasi ketika yang terakhir tidak dapat digunakan.Adapun pertanyaan "kapan saya harus menggunakannya?", Jawabannya adalah Anda seharusnya tidak, kecuali Anda tahu Anda harus melakukannya. :-) EDIT : Ok, ok, berikut adalah beberapa contoh dari perpustakaan. Pada
Either
, Anda memiliki:Pada
Option
, Anda memiliki:Anda akan menemukan beberapa contoh lain di koleksi.
sumber
:-)
salah satunya? Dan saya akan setuju bahwa jawaban Anda untuk "Kapan saya harus menggunakannya?" berlaku untuk banyak hal.