Salah satu cara yang disarankan untuk menangani definisi ganda dari metode kelebihan beban adalah mengganti kelebihan beban dengan pencocokan pola:
object Bar {
def foo(xs: Any*) = xs foreach {
case _:String => println("str")
case _:Int => println("int")
case _ => throw new UglyRuntimeException()
}
}
Pendekatan ini mengharuskan kami menyerahkan tipe statis memeriksa argumen untuk foo
. Akan jauh lebih baik untuk bisa menulis
object Bar {
def foo(xs: (String or Int)*) = xs foreach {
case _: String => println("str")
case _: Int => println("int")
}
}
Saya bisa akrab dengan Either
, tetapi menjadi cepat jelek dengan lebih dari dua jenis:
type or[L,R] = Either[L,R]
implicit def l2Or[L,R](l: L): L or R = Left(l)
implicit def r2Or[L,R](r: R): L or R = Right(r)
object Bar {
def foo(xs: (String or Int)*) = xs foreach {
case Left(l) => println("str")
case Right(r) => println("int")
}
}
Sepertinya seorang jenderal (elegan, efisien) solusi akan membutuhkan mendefinisikan Either3
, Either4
, .... Apakah ada yang tahu dari solusi alternatif untuk mencapai tujuan yang sama? Setahu saya, Scala tidak memiliki "type disjunction" bawaan. Juga, apakah konversi implisit yang didefinisikan di atas bersembunyi di perpustakaan standar di suatu tempat sehingga saya bisa mengimpornya?
class StringOrInt[T]
dibuatsealed
, "kebocoran" yang Anda maksudkan ("Tentu saja, ini bisa digerakkan oleh kode klien dengan membuatStringOrInt[Boolean]
") dicolokkan, setidaknya jikaStringOrInt
berada di file sendiri. Maka objek saksi harus didefinisikan dalam sumber yang sama denganStringOrInt
.Either
pendekatan tampaknya adalah bahwa kami kehilangan banyak dukungan kompiler untuk memeriksa pertandingan.trait StringOrInt ...
StringOrInt[T]
keStringOrInt[-T]
(lihat stackoverflow.com/questions/24387701/… )Miles Sabin menjelaskan cara yang sangat bagus untuk mendapatkan jenis serikat di posting blog terbarunya Jenis serikat tanpa kotak di Scala melalui isomorfisma Curry-Howard :
Dia pertama-tama mendefinisikan negasi jenis sebagai
menggunakan hukum De Morgan, ini memungkinkannya untuk menentukan jenis serikat pekerja
Dengan konstruksi bantu berikut
Anda dapat menulis jenis serikat sebagai berikut:
sumber
Dotty , kompiler Scala eksperimental baru, mendukung tipe union (tertulis
A | B
), sehingga Anda dapat melakukan apa yang Anda inginkan:sumber
Berikut adalah cara Rex Kerr untuk menyandikan tipe serikat. Lurus dan sederhana!
Sumber: Komentar # 27 di bawah posting blog yang sangat baik ini oleh Miles Sabin yang menyediakan cara lain untuk meng-encode jenis-jenis serikat di Scala.
sumber
scala> f(9.2: AnyVal)
melewati pengetikan.trait Contra[-A] {}
semua fungsi untuk menggantikannya. Jadi Anda mendapatkan barang-barang sepertitype Union[A,B] = { type Check[Z] = Contra[Contra[Z]] <:< Contra[Contra[A] with Contra[B]] }
bekas sepertidef f[T: Union[Int, String]#Check](t: T) = t match { case i: Int => i; case s: String => s.length }
(tanpa mewah unicode).Dimungkinkan untuk menggeneralisasi solusi Daniel sebagai berikut:
Kelemahan utama dari pendekatan ini adalah
Either
pendekatan, generalisasi lebih lanjut akan membutuhkan mendefinisikan analogOr3
,Or4
, dll sifat. Tentu saja, mendefinisikan sifat-sifat seperti itu akan jauh lebih sederhana daripada mendefinisikanEither
kelas yang sesuai .Memperbarui:
Mitch Blevins menunjukkan pendekatan yang sangat mirip dan menunjukkan bagaimana menggeneralisasikannya ke lebih dari dua jenis, menyebutnya "gagap atau".
sumber
Saya agak tersandung pada implementasi yang relatif bersih dari tipe-tipe serikat pekerja dengan menggabungkan konsep daftar tipe dengan penyederhanaan pekerjaan Miles Sabin di area ini , yang seseorang sebutkan dalam jawaban lain.
Jenis
¬[-A]
yang diberikan bersifat contravarianA
, dengan definisi yang diberikanA <: B
kita dapat menulis¬[B] <: ¬[A]
, membalik urutan jenis.Jenis diberikan
A
,B
danX
, kami ingin mengungkapkanX <: A || X <: B
. Kami menerapkan contravariance¬[A] <: ¬[X] || ¬[B] <: ¬[X]
. Kaleng pada gilirannya ini dinyatakan sebagai¬[A] with ¬[B] <: ¬[X]
yang salah satuA
atauB
harus menjadi supertype dariX
atauX
sendiri (berpikir tentang argumen fungsi).Aku menghabiskan waktu berusaha untuk menggabungkan ide ini dengan batas atas jenis anggota seperti yang terlihat di
TList
s dari harrah / up , namun pelaksanaanMap
dengan jenis batas-batas sejauh ini terbukti menantang.sumber
Function1
jenis contravarian yang ada. Anda tidak perlu implementasi, yang Anda butuhkan hanyalah bukti kesesuaian (<:<
).Solusi kelas tipe mungkin cara terbaik untuk pergi ke sini, menggunakan implisit. Ini mirip dengan pendekatan monoid yang disebutkan dalam buku Odersky / Spoon / Venners:
Jika Anda menjalankan ini di REPL:
sumber
Either
cenderung memperkuat keyakinan ini. Menggunakan kelas tipe melalui implisit Scala adalah solusi yang lebih baik untuk masalah yang mendasarinya, tetapi ini adalah konsep yang relatif baru dan masih belum diketahui secara luas, itulah mengapa OP bahkan tidak tahu untuk menganggapnya sebagai alternatif yang mungkin untuk tipe serikat.Kami ingin operator tipe
Or[U,V]
yang dapat digunakan untuk membatasi parameter tipeX
sedemikian rupa sehingga dapatX <: U
atauX <: V
. Inilah definisi yang sedekat yang bisa kita dapatkan:Inilah cara penggunaannya:
Ini menggunakan beberapa trik tipe Scala. Yang utama adalah penggunaan batasan tipe umum . Jenis yang diberikan
U
danV
, kompilator Scala menyediakan kelas yang disebutU <:< V
(dan objek implisit dari kelas itu) jika dan hanya jika kompiler Scala dapat membuktikan bahwa ituU
adalah subtipe dariV
. Berikut adalah contoh sederhana menggunakan batasan tipe umum yang berfungsi untuk beberapa kasus:Contoh ini berfungsi ketika
X
turunan kelasB
, aString
, atau memiliki tipe yang bukan tipe supert atau subtipe dariB
atauString
. Dalam dua kasus pertama, itu benar dengan definisiwith
kata kunci itu(B with String) <: B
dan(B with String) <: String
, jadi Scala akan memberikan objek implisit yang akan diteruskan sebagaiev
: kompiler Scala akan menerimafoo[B]
dan dengan benarfoo[String]
.Dalam kasus terakhir, saya mengandalkan fakta bahwa jika
U with V <: X
, makaU <: X
atauV <: X
. Tampaknya secara intuitif benar, dan saya hanya mengasumsikannya. Dari asumsi ini jelas mengapa contoh sederhana ini gagal ketikaX
supertipe atau subtipe dari salah satuB
atauString
: misalnya, dalam contoh di atas,foo[A]
diterima secara tidak benar danfoo[C]
ditolak secara salah. Sekali lagi, apa yang kita inginkan adalah semacam ekspresi tipe pada variabelU
,V
danX
itu benar kapan tepatnyaX <: U
atauX <: V
.Gagasan tentang kontradiksi Scala dapat membantu di sini. Ingat sifatnya
trait Inv[-X]
? Karena parameter parameternya contravarianX
,Inv[X] <: Inv[Y]
jika dan hanya jikaY <: X
. Itu berarti bahwa kita dapat mengganti contoh di atas dengan yang benar-benar berfungsi:Itu karena ungkapan
(Inv[U] with Inv[V]) <: Inv[X]
itu benar, dengan asumsi yang sama di atas, tepat kapanInv[U] <: Inv[X]
atauInv[V] <: Inv[X]
, dan dengan definisi contravariance, ini benar tepat ketikaX <: U
atauX <: V
.Dimungkinkan untuk membuat hal-hal sedikit lebih dapat digunakan kembali dengan mendeklarasikan tipe parametrizable
BOrString[X]
dan menggunakannya sebagai berikut:Scala sekarang akan mencoba untuk membangun tipe
BOrString[X]
untuk setiapX
yangfoo
dipanggil dengan, dan tipe tersebut akan dibangun tepat ketikaX
subtipe dari salah satuB
atauString
. Itu bekerja, dan ada notasi steno. Sintaks di bawah ini setara (kecuali yangev
sekarang harus direferensikan dalam tubuh metode sebagaiimplicitly[BOrString[X]]
bukan hanyaev
) dan digunakanBOrString
sebagai tipe konteks terikat :Apa yang benar-benar kita sukai adalah cara yang fleksibel untuk membuat konteks tipe terikat. Konteks tipe harus merupakan tipe parametrizable, dan kami ingin cara parametrizable untuk membuatnya. Kedengarannya seperti kita sedang mencoba menjilat fungsi pada tipe seperti kita menjilat fungsi pada nilai. Dengan kata lain, kami ingin sesuatu seperti yang berikut:
Itu tidak mungkin secara langsung di Scala, tetapi ada trik yang bisa kita gunakan untuk menjadi cukup dekat. Itu membawa kita ke definisi di
Or
atas:Di sini kita menggunakan pengetikan struktural dan operator pound Scala untuk membuat tipe struktural
Or[U,T]
yang dijamin memiliki satu tipe internal. Ini adalah binatang aneh. Untuk memberikan beberapa konteks, fungsi tersebutdef bar[X <: { type Y = Int }](x : X) = {}
harus dipanggil dengan subkelasAnyRef
yang memiliki tipe yangY
ditentukan di dalamnya:Menggunakan operator pon memungkinkan kita untuk merujuk ke tipe dalam
Or[B, String]#pf
, dan menggunakan notasi infiks untuk operator tipeOr
, kita sampai pada definisi asli kita tentangfoo
:Kita dapat menggunakan fakta bahwa tipe fungsi bersifat contravarian dalam parameter tipe pertama mereka untuk menghindari mendefinisikan sifat
Inv
:sumber
A|B <: A|B|C
masalah? stackoverflow.com/questions/45255270/… Saya tidak tahu.Ada juga retasan ini :
Lihat Mengatasi ambiguitas penghapusan tipe (Scala) .
sumber
(implicit e: DummyImplicit)
ke salah satu tanda tangan jenis.Anda mungkin melihat MetaScala , yang memiliki sesuatu yang disebut
OneOf
. Saya mendapat kesan bahwa ini tidak bekerja dengan baik denganmatch
pernyataan tetapi Anda dapat mensimulasikan pencocokan menggunakan fungsi tingkat tinggi. Sebagai contoh, lihat potongan ini , tetapi perhatikan bahwa bagian "pencocokan simulasi" dikomentari, mungkin karena belum berfungsi.Sekarang untuk beberapa editorial: Saya tidak berpikir ada sesuatu yang mengerikan tentang mendefinisikan Either3, Either4, dll seperti yang Anda gambarkan. Ini pada dasarnya ganda untuk 22 jenis tuple standar yang dibangun untuk Scala. Pasti menyenangkan jika Scala memiliki tipe disjungtif bawaan, dan mungkin beberapa sintaks yang bagus untuk mereka
{x, y, z}
.sumber
Saya berpikir bahwa tipe disjoint kelas pertama adalah supertipe tertutup, dengan subtipe alternatif, dan konversi implisit ke / dari tipe disjunction yang diinginkan ke subtipe alternatif ini.
Saya menganggap ini membahas komentar 33 - 36 dari solusi Miles Sabin, jadi tipe kelas pertama yang dapat digunakan di situs penggunaan, tetapi saya tidak mengujinya.
Satu masalah adalah Scala tidak akan menggunakan dalam konteks kasus pencocokan, konversi implisit dari
IntOfIntOrString
keInt
(danStringOfIntOrString
keString
), jadi harus mendefinisikan extractors dan menggunakancase Int(i)
alih-alihcase i : Int
.TAMBAH: Saya merespons Miles Sabin di blognya sebagai berikut. Mungkin ada beberapa perbaikan di antaranya:
size(Left(2))
atausize(Right("test"))
.V
daripadaOr
, misalnyaIntVString
, `Int |v| String
`, `Int or String
`, atau favorit saya `Int|String
`?UPDATE: Negasi logis dari disjungsi untuk pola di atas mengikuti, dan saya menambahkan alternatif (dan mungkin lebih bermanfaat) pola di blog Miles Sabin .
PEMBARUAN LAIN: Mengenai komentar 23 dan 35 dari solusi Mile Sabin , berikut adalah cara untuk mendeklarasikan jenis persatuan di situs penggunaan. Catat itu tidak dikotak setelah tingkat pertama, yaitu memiliki keuntungan yang dapat diperpanjang untuk sejumlah jenis dalam disjungsi , sedangkan
Either
kebutuhan bertinju tinju dan paradigma dalam komentar saya sebelumnya 41 tidak dapat diperpanjang. Dengan kata lain, aD[Int ∨ String]
ditugaskan untuk (yaitu subtipe dari) aD[Int ∨ String ∨ Double]
.Rupanya kompiler Scala memiliki tiga bug.
D[¬[Double]]
kasing dari pertandingan.3.
Metode get tidak dibatasi dengan benar pada tipe input, karena kompiler tidak akan memungkinkan
A
dalam posisi kovarian. Orang mungkin berpendapat bahwa itu adalah bug karena yang kita inginkan hanyalah bukti, kita tidak pernah mengakses bukti dalam fungsinya. Dan saya membuat pilihan untuk tidak mengujicase _
dalamget
metode ini, jadi saya tidak perlu membukaOption
kotakmatch
di dalamsize()
.05 Maret 2012: Pembaruan sebelumnya perlu ditingkatkan. Solusi Miles Sabin bekerja dengan benar dengan subtyping.
Proposal pembaruan saya sebelumnya (untuk tipe persatuan kelas dekat) mematahkan subtipe.
Masalahnya adalah bahwa
A
dalam(() => A) => A
muncul di kedua kovarian (tipe kembali) dan contravarian (input fungsi, atau dalam hal ini nilai balik fungsi yang merupakan input fungsi) posisi, sehingga substitusi hanya dapat invarian.Perhatikan bahwa
A => Nothing
diperlukan hanya karena kita inginA
di posisi contravariant, sehingga supertypes dariA
tidak subtipe dariD[¬[A]]
atauD[¬[A] with ¬[U]]
( lihat juga ). Karena kita hanya memerlukan kontravarian ganda, kita dapat mencapai yang setara dengan solusi Miles bahkan jika kita dapat membuang¬
dan∨
.Jadi perbaikan lengkapnya adalah.
Catat 2 bug sebelumnya di Scala tetap, tetapi yang ke 3 dihindari karena
T
sekarang dibatasi menjadi subtipeA
.Kami dapat mengkonfirmasi karya subtyping.
Saya telah berpikir bahwa jenis persimpangan kelas sangat penting, baik untuk alasan Ceylon memiliki mereka , dan karena bukannya subsuming untuk
Any
yang berarti pembukaan kemasan denganmatch
pada jenis diharapkan dapat menghasilkan runtime error, yang unboxing dari ( koleksi heterogen yang mengandung a) disjungsi dapat diketikkan jenisnya (Scala harus memperbaiki bug yang saya catat). Serikat pekerja yang lebih mudah daripada kompleksitas menggunakan eksperimental HList dari metascala untuk koleksi heterogen.sumber
size
fungsi yang berbeda .size
tidak lagi menerimaD[Any]
sebagai masukan.Ada cara lain yang sedikit lebih mudah dipahami jika Anda tidak grok Curry-Howard:
Saya menggunakan teknik serupa di dijon
sumber
Yah, itu semua sangat pintar, tapi saya cukup yakin Anda sudah tahu bahwa jawaban atas pertanyaan utama Anda adalah berbagai variasi "Tidak". Scala menangani kelebihan beban secara berbeda dan, harus diakui, agak kurang elegan dari yang Anda gambarkan. Beberapa di antaranya karena interoperabilitas Java, beberapa di antaranya adalah karena tidak ingin menekan kasus bermata jenis algoritma menyimpulkan, dan beberapa di antaranya karena itu tidak menjadi Haskell.
sumber
Menambah jawaban yang sudah bagus di sini. Berikut adalah intisari yang dibangun di atas tipe serikat Miles Sabin (dan ide-ide Josh) tetapi juga membuat mereka didefinisikan secara rekursif, sehingga Anda dapat memiliki> 2 jenis di serikat (
def foo[A : UNil Or Int Or String Or List[String]
)https://gist.github.com/aishfenton/2bb3bfa12e0321acfc904a71dda9bfbb
NB: Saya harus menambahkan bahwa setelah bermain-main dengan di atas untuk sebuah proyek, saya akhirnya kembali ke tipe-biasa-lama-sum (yaitu sifat disegel dengan subclass). Jenis union Miles Sabin sangat bagus untuk membatasi parameter type, tetapi jika Anda perlu mengembalikan tipe union maka itu tidak menawarkan banyak.
sumber
A|C <: A|B|C
masalah subtyping? stackoverflow.com/questions/45255270/... Perasaan saya TIDAK karena itu berarti bahwaA or C
itu harus menjadi subtipe dari(A or B) or C
tetapi itu tidak mengandung tipeA or C
sehingga tidak ada harapan dalam membuatA or C
subtipeA or B or C
dengan pengkodean ini setidaknya .. . Bagaimana menurut anda ?Dari dokumen , dengan tambahan
sealed
:Mengenai
sealed
bagian:sumber