Setiap nilai di Julia membawa satu tipe, dan seluruh tipe itu tersusun sebagai pohon yang berakar pada Any. Topik ini memetakan relasi antartipe dalam pohon tersebut, membedakan tipe abstrak dari tipe konkret, serta memperkenalkan operator <: beserta fungsi supertype dan subtypes untuk memeriksa relasi tersebut. Setelah topik ini selesai, struktur sistem tipe Julia dapat dibaca secara sistematis, termasuk letak tipe bilangan yang dipakai sehari-hari.
Tipe konkret dan tipe abstrak#
Tipe konkret (concrete type) adalah tipe yang dapat diinstansiasi, artinya nilai dapat dibuat darinya. Tipe abstrak (abstract type) tidak dapat menyimpan nilai dan hanya berfungsi sebagai penanda kelompok tipe yang berbagi sifat. Fungsi typeof mengembalikan tipe konkret dari sebuah nilai, sedangkan fungsi isconcretetype memeriksa apakah sebuah tipe dapat diinstansiasi. Contoh berikut memeriksa tipe beberapa nilai yang umum dipakai.
Setiap nilai yang dibuat, baik dari literal maupun hasil perhitungan, langsung membawa satu tipe konkret. Tipe abstrak justru hanya muncul pada anotasi tipe dan pada definisi tipe, tidak pernah pada nilai tunggal. Pembedaan ini membuat typeof selalu punya jawaban pasti untuk nilai apa pun.
println(typeof(42))
println(typeof(3.14))
println(typeof("teks"))
println(typeof('a'))
println(isconcretetype(Int64))
println(isconcretetype(Integer))Int64
Float64
String
Char
true
falseNilai 42 bertipe Int64, nilai 3.14 bertipe Float64, dan “teks” bertipe String, sehingga typeof selalu menjawab dengan tipe konkret. Pemeriksaan dua baris terakhir memperlihatkan kontrasnya, yaitu Int64 konkret sedangkan Integer abstrak. Tidak ada nilai tunggal yang bertipe persis Integer, tetapi banyak nilai bertipe di bawah Integer seperti Int64 dan Bool. Pemisahan ini menjadi dasar untuk membaca pohon tipe pada bagian berikutnya.
Relasi subtype dan supertype#
Tipe T disebut subtype dari tipe U apabila setiap nilai bertipe T juga sah sebagai nilai bertipe U. Julia menuliskan relasi itu dengan operator <:, sehingga ekspresi Int64 <: Integer dibaca “Int64 adalah subtype dari Integer”. Operator yang sama dipakai pada saat deklarasi untuk menyatakan induk sebuah tipe baru. Untuk pemeriksaan nilai, fungsi isa menanyakan apakah sebuah nilai termasuk dalam sebuah tipe, sedangkan fungsi supertype mengembalikan induk langsung sebuah tipe.
println(Int64 <: Integer)
println(Float64 <: Real)
println(Integer <: Real)
println(Bool <: Integer)
println(String <: Number)
println(isa(42, Integer))
println(isa(42, AbstractFloat))true
true
true
true
false
true
falseSemua relasi bernilai true kecuali dua yang terakhir. Hasil String <: Number bernilai false karena String berada di luar pohon tipe bilangan. Nilai 42 memenuhi isa(42, Integer), tetapi bukan anggota AbstractFloat, sehingga kedua kelompok bilangan itu terpisah meskipun sama-sama berada di bawah Real. Perhatikan pula bahwa Bool adalah subtype dari Integer karena nilai true dan false diperlakukan sebagai 1 dan 0.
Relasi subtype bersifat transitif, sehingga Int64 yang berada di bawah Integer otomatis menjadi subtype dari Real dan Number tanpa dinyatakan langsung. Operator <: mengikuti rantai induk tersebut sampai ke Any, sedangkan isa menerapkan rantai yang sama pada nilai. Dengan dua alat ini, posisi sebuah tipe maupun sebuah nilai pada pohon dapat ditelusuri tanpa menghafal daftar lengkapnya.
Pohon tipe di bawah Any#
Seluruh tipe di Julia berakar pada Any, tipe abstrak paling atas yang menjadi induk langsung maupun tak langsung dari semua tipe. Di bawahnya terdapat Number, kemudian Real yang mencakup Integer dan AbstractFloat. Cabang Integer memuat Signed, Unsigned, dan Bool, sedangkan cabang AbstractFloat memuat Float32 dan Float64. Diagram berikut memperlihatkan potongan pohon tersebut, dengan kotak putus-putus untuk tipe abstrak dan kotak penuh untuk tipe konkret.
Fungsi subtypes mengembalikan daftar anak langsung sebuah tipe abstrak, sedangkan supertype dapat dipanggil berulang untuk menaiki pohon hingga tiba di Any. Contoh berikut menaiki pohon dari Int64, lalu menelusuri cabang-cabang di bawah Real. Fungsi subtypes berada di pustaka standar InteractiveUtils, sehingga skrip perlu memuat pustaka tersebut terlebih dahulu.
using InteractiveUtils
println(supertype(Int64))
println(supertype(Signed))
println(supertype(Real))
println(supertype(Any))
println(subtypes(Integer))
println(subtypes(Real))
println(subtypes(AbstractFloat))Signed
Integer
Number
Any
Any[Bool, Signed, Unsigned]
Any[AbstractFloat, AbstractIrrational, Integer, Rational]
Any[BigFloat, Core.BFloat16, Float16, Float32, Float64]Rangkaian pemanggilan supertype menaiki pohon dari Int64 menuju Signed, Integer, Real, Number, dan berhenti di Any karena Any adalah akar pohon. Daftar subtypes(Real) juga memuat AbstractIrrational dan Rational, dua cabang bilangan yang belum tersentuh pada contoh. Sementara itu, subtypes(AbstractFloat) memuat tipe pecahan mulai dari presisi rendah seperti Float16 hingga BigFloat dengan presisi arbitrer.
Tipe abstrak tidak dapat diinstansiasi#
Deklarasi tipe abstrak baru memakai kata kunci abstract type, dan tipe baru dapat diletakkan di bawah tipe lain dengan operator <:. Karena tidak memiliki konstruktor, tipe abstrak tidak dapat menjadi tempat penyimpan nilai. Percobaan membuat nilai dari tipe abstrak memunculkan MethodError. Contoh berikut mendeklarasikan dua tipe abstrak untuk status ujian, kemudian mencoba menginstansiasi induknya.
abstract type StatusUjian end
abstract type StatusLulus <: StatusUjian end
println(isabstracttype(StatusUjian))
println(StatusLulus <: StatusUjian)
try
StatusUjian()
catch e
println(typeof(e))
println(split(sprint(showerror, e), "\n")[1])
endtrue
true
MethodError
MethodError: no constructors have been defined for StatusUjianPesan error menyatakan bahwa tidak ada konstruktor yang didefinisikan untuk StatusUjian. Relasi StatusLulus <: StatusUjian tetap sah, sehingga tipe abstrak tetap berguna sebagai penanda kelompok pada anotasi tipe dan pada pemilihan metode. Nilai yang benar-benar menyimpan data selalu dibuat dari tipe konkret, misalnya Int64 atau struct buatan pengguna.
Pembatasan ini disengaja. Tipe abstrak menetapkan kontrak berupa kelompok tipe, sedangkan pembuatan nilai diserahkan kepada tipe konkret di bawahnya. Apabila kelompok baru diperlukan, tipe konkret yang sesuai ditambahkan di bawah tipe abstrak tersebut, dan anotasi bertipe abstrak otomatis mencakup seluruh turunannya tanpa perubahan apa pun.
Kesalahan umum#
Tiga kesalahan berikut paling sering muncul saat memeriksa hierarki tipe. Masing-masing mudah dihindari setelah penyebabnya dikenali.
- Memanggil
subtypespada skrip tanpa memuatInteractiveUtilsterlebih dahulu. Julia menampilkanUndefVarErrorkarena fungsi itu tidak disediakan olehBase. Tambahkan barisusing InteractiveUtilsdi awal skrip. - Membalik arah operator
<:. EkspresiInteger <: Int64bernilaifalsekarena yang ditanyakan adalah apakahIntegerberada di bawahInt64, padahal relasinya sebaliknya. Baca operator itu sebagai “kiri berada di bawah kanan”. - Mengira tipe abstrak dapat menampung nilai. Pernyataan seperti
Integer()memunculkanMethodError, sehingga nilai harus dibuat dari tipe konkret. Anotasi tipe abstrak sepertix::Integertetap sah dan membatasi variabel pada seluruh turunannya.
Ketiganya bermuara pada kebiasaan yang sama, yaitu membaca relasi dari bawah ke atas. Sebuah nilai selalu bertipe konkret, sedangkan tipe yang dituliskan pada anotasi boleh abstrak, asalkan tipe konkret nilainya berada di bawah tipe abstrak itu. Dengan membiasakan arah baca ini, pemeriksaan hierarki menjadi rutin yang cepat.
Ringkasan#
- Tipe konkret dapat diinstansiasi dan dikenali melalui
typeof, sedangkan tipe abstrak hanya menandai kelompok tipe dan diperiksa denganisabstracttype. - Relasi antartipe diperiksa dengan operator
<:beserta fungsiisadansupertype, sedangkansubtypesberada di pustakaInteractiveUtils. - Seluruh tipe berakar pada
Any, dan pohon bilangan menurun dariNumberkeReallalu bercabang keIntegerdanAbstractFloat. - Tipe abstrak tidak dapat diinstansiasi, sehingga nilai selalu dibuat dari tipe konkret di bawahnya.
Referensi#
- Types — Julia Documentation — Penjelasan tipe abstrak, tipe konkret, dan deklarasinya.
- InteractiveUtils — Julia Documentation
— Pustaka standar yang memuat fungsi
subtypesbeserta utilitas refleksi lainnya.