Kenyamanan sintaksis samping, kombinasi jenis tunggal, jenis jalan-dependent dan nilai-nilai implisit berarti bahwa Scala memiliki dukungan mengejutkan baik untuk mengetik tergantung, karena saya sudah mencoba untuk menunjukkan di berbentuk .
Dukungan intrinsik Scala untuk tipe dependen adalah melalui tipe yang bergantung pada jalur . Ini memungkinkan tipe bergantung pada jalur pemilih melalui grafik objek- (mis. Nilai-) seperti itu,
scala> class Foo { class Bar }
defined class Foo
scala> val foo1 = new Foo
foo1: Foo = Foo@24bc0658
scala> val foo2 = new Foo
foo2: Foo = Foo@6f7f757
scala> implicitly[foo1.Bar =:= foo1.Bar] // OK: equal types
res0: =:=[foo1.Bar,foo1.Bar] = <function1>
scala> implicitly[foo1.Bar =:= foo2.Bar] // Not OK: unequal types
<console>:11: error: Cannot prove that foo1.Bar =:= foo2.Bar.
implicitly[foo1.Bar =:= foo2.Bar]
Dalam pandangan saya, pertanyaan di atas seharusnya cukup untuk menjawab pertanyaan "Apakah Scala merupakan bahasa yang diketik dengan ketergantungan?" di positif: jelas bahwa di sini kami memiliki tipe yang dibedakan oleh nilai yang merupakan awalannya.
Namun, sering kali ada keberatan bahwa Scala bukanlah bahasa jenis yang "sepenuhnya" bergantung karena tidak memiliki jumlah dan jenis produk yang bergantung seperti yang ditemukan di Agda atau Coq atau Idris sebagai intrinsik. Saya pikir ini mencerminkan fiksasi pada bentuk atas dasar-dasar sampai batas tertentu, namun, saya akan mencoba dan menunjukkan bahwa Scala jauh lebih dekat dengan bahasa lain ini daripada yang biasanya diakui.
Terlepas dari terminologi, tipe penjumlahan dependen (juga dikenal sebagai tipe Sigma) hanyalah sepasang nilai di mana tipe nilai kedua bergantung pada nilai pertama. Ini secara langsung dapat direpresentasikan di Scala,
scala> trait Sigma {
| val foo: Foo
| val bar: foo.Bar
| }
defined trait Sigma
scala> val sigma = new Sigma {
| val foo = foo1
| val bar = new foo.Bar
| }
sigma: java.lang.Object with Sigma{val bar: this.foo.Bar} = $anon$1@e3fabd8
dan faktanya, ini adalah bagian penting dari pengkodean tipe metode dependen yang diperlukan untuk keluar dari 'Bakery of Doom' di Scala sebelum 2.10 (atau sebelumnya melalui opsi compiler Scala tipe metode dependen eksperimental).
Tipe produk dependen (alias tipe Pi) pada dasarnya adalah fungsi dari nilai ke tipe. Mereka adalah kunci untuk representasi vektor berukuran statis dan turunan poster lainnya untuk bahasa pemrograman yang diketik secara dependen. Kita bisa mengenkode tipe Pi di Scala menggunakan kombinasi tipe path dependent, tipe singleton dan parameter implisit. Pertama kita mendefinisikan sifat yang akan mewakili fungsi dari nilai tipe T ke tipe U,
scala> trait Pi[T] { type U }
defined trait Pi
Kita dapat mendefinisikan metode polimorfik yang menggunakan tipe ini,
scala> def depList[T](t: T)(implicit pi: Pi[T]): List[pi.U] = Nil
depList: [T](t: T)(implicit pi: Pi[T])List[pi.U]
(perhatikan penggunaan tipe yang bergantung pi.U
pada jalur dalam tipe hasil List[pi.U]
). Diberikan nilai tipe T, fungsi ini akan mengembalikan (n kosong) daftar nilai tipe yang sesuai dengan nilai T tertentu.
Sekarang mari kita tentukan beberapa nilai yang sesuai dan saksi implisit untuk hubungan fungsional yang ingin kita pegang,
scala> object Foo
defined module Foo
scala> object Bar
defined module Bar
scala> implicit val fooInt = new Pi[Foo.type] { type U = Int }
fooInt: java.lang.Object with Pi[Foo.type]{type U = Int} = $anon$1@60681a11
scala> implicit val barString = new Pi[Bar.type] { type U = String }
barString: java.lang.Object with Pi[Bar.type]{type U = String} = $anon$1@187602ae
Dan sekarang inilah fungsi penggunaan tipe-Pi kami yang sedang beraksi,
scala> depList(Foo)
res2: List[fooInt.U] = List()
scala> depList(Bar)
res3: List[barString.U] = List()
scala> implicitly[res2.type <:< List[Int]]
res4: <:<[res2.type,List[Int]] = <function1>
scala> implicitly[res2.type <:< List[String]]
<console>:19: error: Cannot prove that res2.type <:< List[String].
implicitly[res2.type <:< List[String]]
^
scala> implicitly[res3.type <:< List[String]]
res6: <:<[res3.type,List[String]] = <function1>
scala> implicitly[res3.type <:< List[Int]]
<console>:19: error: Cannot prove that res3.type <:< List[Int].
implicitly[res3.type <:< List[Int]]
(perhatikan bahwa di sini kami menggunakan <:<
operator menyaksikan subtipe Scala daripada =:=
karena res2.type
dan res3.type
merupakan tipe tunggal dan karenanya lebih tepat daripada tipe yang kami verifikasi di RHS).
Dalam praktiknya, bagaimanapun, di Scala kami tidak akan mulai dengan mengkodekan tipe Sigma dan Pi dan kemudian melanjutkan dari sana seperti yang kami lakukan di Agda atau Idris. Sebagai gantinya kita akan menggunakan tipe yang bergantung pada jalur, tipe tunggal dan implikasinya secara langsung. Anda dapat menemukan banyak contoh bagaimana hal ini dimainkan dalam bentuk tak berbentuk: tipe ukuran , catatan yang dapat diperluas , Daftar HL yang komprehensif , memo plat boiler Anda , Resleting generik dll. Dll.
Keberatan yang tersisa yang dapat saya lihat adalah bahwa dalam pengkodean tipe Pi di atas kita membutuhkan tipe tunggal dari nilai dependen agar dapat diekspresikan. Sayangnya di Scala ini hanya mungkin untuk nilai tipe referensi dan bukan untuk nilai tipe non-referensi (esp. Misalnya Int). Ini adalah rasa malu, tapi bukan kesulitan intrinsik: Scala jenis checker merupakan jenis tunggal dari nilai-nilai non-referensi internal, dan telah ada beberapa dari percobaan dalam membuat mereka secara langsung dinyatakan. Dalam praktiknya kita dapat mengatasi masalah dengan pengkodean tingkat tipe yang cukup standar dari bilangan asli .
Bagaimanapun, menurut saya batasan domain kecil ini tidak dapat digunakan sebagai keberatan terhadap status Scala sebagai bahasa yang diketik secara dependen. Jika ya, maka hal yang sama dapat dikatakan untuk Dependent ML (yang hanya memungkinkan ketergantungan pada nilai bilangan asli) yang akan menjadi kesimpulan yang aneh.