Baiklah, mari kita pergi satu per satu.
Nilai-nilai
Nilai adalah bagian konkret dari data yang dievaluasi dan disulap oleh program. Tidak ada yang mewah, beberapa contoh mungkin
1
true
"fizz buzz foo bar"
Jenis
Deskripsi yang bagus untuk suatu tipe adalah "classifier for a value". Tipe adalah sedikit informasi tentang nilai tersebut pada saat runtime, tetapi ditunjukkan pada waktu kompilasi.
Sebagai contoh jika Anda memberi tahu saya bahwa e : bool
pada waktu kompilasi, dan saya akan tahu itu e
baik true
atau false
selama runtime, tidak ada yang lain! Karena tipe mengklasifikasikan nilai dengan baik seperti ini, kami dapat menggunakan informasi ini untuk menentukan beberapa properti dasar dari program Anda.
Misalnya, jika saya melihat Anda menambahkan e
dan e'
kapan e : int
dan e' : String
, maka saya tahu ada sesuatu yang salah! Sebenarnya saya bisa menandai ini dan melemparkan kesalahan pada waktu kompilasi, mengatakan "Hei, itu tidak masuk akal sama sekali!".
Sistem tipe yang lebih kuat memungkinkan tipe yang lebih menarik yang mengklasifikasikan nilai yang lebih menarik. Sebagai contoh, mari kita pertimbangkan beberapa fungsi
f = fun x -> x
Sudah cukup jelas f : Something -> Something
, tetapi apa yang seharusnya Something
? Dalam sistem tipe yang membosankan, kita harus menentukan sesuatu yang sewenang-wenang Something = int
. Dalam sistem tipe yang lebih fleksibel, bisa dikatakan
f : forall a. a -> a
Artinya "untuk apa pun a
, f
memetakan suatu a
ke a
". Ini mari kita gunakan secara f
lebih umum dan menulis program yang lebih menarik.
Selain itu, kompiler akan memeriksa benar-benar memuaskan penggolong yang telah kita berikan, jika f = fun x -> true
kemudian kita memiliki bug dan kompiler akan berkata begitu!
Jadi sebagai tldr; tipe adalah batasan waktu kompilasi pada nilai-nilai yang bisa diekspresikan saat runtime.
Tipe Pembuat
Beberapa jenis terkait. Misalnya daftar bilangan bulat sangat mirip dengan daftar string. Ini hampir seperti bagaimana sort
untuk bilangan bulat hampir seperti sort
untuk string. Kita dapat membayangkan semacam pabrik yang membangun jenis yang hampir sama dengan menggeneralisasi perbedaan mereka dan membangunnya berdasarkan permintaan. Itulah tipe konstruktor. Ini semacam fungsi dari tipe ke tipe, tetapi sedikit lebih terbatas.
Contoh klasik adalah daftar generik. Konstruktor tipe hanya definisi generik
data List a = Cons a (List a) | Nil
Sekarang List
adalah fungsi yang memetakan suatu tipe a
ke daftar nilai dari tipe itu! Di Jawa-tanah saya pikir ini mungkin disebut "kelas generik"
Ketik Parameter
Parameter tipe hanyalah tipe yang diteruskan ke konstruktor tipe (atau fungsi). Sama seperti di level nilai kita akan mengatakan foo(a)
memiliki parameter a
sama seperti bagaimana List a
memiliki parameter tipe a
.
Jenis
Jenisnya agak rumit. Ide dasarnya adalah jenis-jenis tertentu serupa. Sebagai contoh, kita memiliki semua jenis primitif di java int
, char
, float
... yang semua berperilaku seolah-olah mereka memiliki "tipe" yang sama. Kecuali, ketika kita berbicara tentang pengklasifikasi untuk tipe itu sendiri, kita menyebut jenis pengklasifikasi. Jadi int : Prim
, String : Box
, List : Boxed -> Boxed
.
Sistem ini memberikan aturan konkret yang bagus tentang jenis apa yang bisa kita gunakan di mana, seperti bagaimana jenis mengatur nilai. Jelas tidak masuk akal untuk mengatakan
List<List>
atau
List<int>
Di Jawa karena List
perlu diterapkan pada jenis beton untuk digunakan seperti itu! Jika kita melihat jenis mereka List : Boxed -> Boxed
dan karena itu Boxed -> Boxed /= Boxed
, di atas adalah kesalahan jenis!
Sebagian besar waktu kita tidak benar-benar berpikir tentang jenis dan hanya memperlakukan mereka sebagai "akal sehat", tetapi dengan sistem tipe yang lebih bagus itu adalah sesuatu yang penting untuk dipikirkan.
Sedikit ilustrasi tentang apa yang telah saya katakan sejauh ini
value : type : kind : ...
true : bool : Prim : ...
new F() : Foo : Boxed : ...
Bacaan Lebih Baik Dari Wikipedia
Jika Anda tertarik dengan hal semacam ini, saya akan sangat menyarankan berinvestasi buku teks yang bagus. Teori tipe dan PLT secara umum cukup luas dan tanpa dasar pengetahuan yang koheren, Anda (atau setidaknya saya) bisa berkeliaran tanpa harus berbulan-bulan.
Dua buku favorit saya adalah
- Jenis dan Bahasa Pemrograman - Ben Pierce
- Yayasan Praktis Bahasa Pemrograman - Bob Harper
Keduanya adalah buku-buku bagus yang memperkenalkan apa yang baru saja saya bicarakan dan banyak lagi detail yang indah dan dijelaskan dengan baik.
int
di Jawa terdiri dari satu set 2 ^ 64 nilai yang berbeda. Analogi dengan himpunan rusak ketika subtipe terlibat, tetapi itu adalah intuisi awal yang cukup baik, terutama sekali Anda mempertimbangkan tipe data aljabar (misalnya penyatuan dua jenis dapat berisi anggota dari salah satu jenis; ini adalah penyatuan dari set tersebut) .Mereka didefinisikan dengan benar oleh dukungan matematika yang kaku, akademis, memberikan pernyataan yang kuat tentang apa mereka, bagaimana mereka bekerja, dan apa yang dijamin.
Tetapi programmer sebagian besar tidak perlu tahu itu. Mereka perlu memahami konsepnya.
Nilai-nilai
Mari kita mulai dengan nilai, karena semuanya dibangun dari sana. Nilai adalah data yang digunakan dalam komputasi. Bergantung pada pendekatannya, itu adalah nilai-nilai yang dikenal semua orang: 42, 3.14, "Bagaimana sekarang sapi coklat", catatan personil untuk Jenny di bidang Akuntansi, dll.
Interpretasi lain dari nilai adalah simbol . Kebanyakan programmer memahami simbol-simbol ini sebagai "nilai" dari sebuah enumerasi.
Left
danRight
merupakan simbol untuk enumHandedness
(mengabaikan orang dan ikan ambidextrous).Terlepas dari implementasinya, nilai adalah hal-hal berbeda yang digunakan oleh bahasa untuk melakukan perhitungan.
Jenis
Masalah dengan nilai adalah bahwa tidak semua perhitungan legal untuk semua nilai.
42 + goat
tidak masuk akal.Di sinilah tipe ikut bermain. Tipe adalah metadata yang menentukan himpunan bagian dari nilai. The
Handedness
enum di atas adalah contoh yang baik. Jenis ini mengatakan "hanyaLeft
danRight
dapat digunakan di sini". Ini memungkinkan program untuk menentukan sangat awal bahwa operasi tertentu akan menghasilkan kesalahan.Penggunaan praktis lain yang perlu dipertimbangkan adalah bahwa di bawah tenda, komputer bekerja dengan byte. Byte 42 mungkin berarti angka 42, atau mungkin berarti karakter *, atau itu mungkin berarti Jenny dari Akuntansi. Jenis juga (dalam penggunaan praktis, tidak banyak teoritis) membantu mendefinisikan pengkodean untuk koleksi byte yang digunakan oleh komputer.
Jenis
Dan di sinilah kita mulai sedikit keluar. Jadi, ketika sebuah bahasa pemrograman memiliki variabel yang mengacu pada tipe, jenis apa itu memiliki?
Dalam Java dan C # misalnya, ia memiliki tipe
Type
(yang memiliki tipeType
, yang memiliki ... dan seterusnya). Ini adalah konsep di balik jenis . Dalam beberapa bahasa, Anda dapat melakukan sedikit hal yang lebih berguna dengan variabel Type daripada Java dan C #. Setelah itu terjadi menjadi berguna untuk mengatakan "Aku ingin nilai yang Type, tetapi juga beberapa jenis dariIEnumerable<int>
". Ta-da! Jenis.Sebagian besar programmer dapat memikirkan jenis-jenis seperti Java dan batasan generik C #. Pertimbangkan
public class Foo<T> where T: IComparable{}
. Dalam bahasa dengan jenis,T: kindOf(IComparable)
deklarasi variabel menjadi sah; bukan hanya hal khusus yang dapat Anda lakukan di deklarasi kelas dan fungsi.Tipe Konstruktor
Mungkin tidak mengejutkan, konstruktor tipe hanyalah konstruktor untuk tipe . "Tapi bagaimana Anda membangun suatu tipe? Tipe apa adanya ." Eh ... tidak banyak.
Juga tidak mengherankan, cukup sulit untuk membangun semua subset nilai berguna yang berbeda yang akan digunakan oleh program komputer. Ketik konstruktor berfungsi untuk membantu memungkinkan programmer untuk "membangun" subset tersebut dengan cara yang bermakna.
Contoh yang paling mana-mana dari tipe konstruktor adalah definisi array yang:
int[4]
. Di sini Anda menentukan4
konstruktor tipe, yang menggunakan nilai untuk membangun Anda sebuah arrayint
dengan 4 entri. Jika Anda menentukan jenis input yang berbeda, Anda akan mendapatkan tipe output yang berbeda.Generik adalah bentuk lain dari konstruktor tipe, menggunakan tipe lain sebagai inputnya.
Dalam banyak bahasa ada konstruktor tipe suka
P -> R
membangun tipe yang mewakili fungsi yang mengambil tipeP
dan mengembalikan tipeR
.Sekarang, konteksnya akan menentukan apakah "fungsi yang mengembalikan tipe" adalah konstruktor tipe atau tidak. Dalam pengalaman saya (memang terbatas), barisnya adalah "bisakah Anda menggunakan jenis ini pada waktu kompilasi?". Iya? Ketik konstruktor. Tidak? Hanya sebuah fungsi.
Ketikkan Parameter
Jadi Anda ingat parameter yang diteruskan ke Tipe Konstruktor? Mereka umumnya dikenal sebagai Type Parameters, karena bentuk umum dari Type Constructor adalah
Type[param]
atauType<param>
.sumber
*
, sedangkan konstruktor tipe (dengan satu argumen) memiliki jenis* -> *
. Kendala seperti(Num a) => a
(yang berarti "jenis apa puna
yang merupakan turunan dariNum
typeclass") bukan jenis sendiri. TypeclassNum
bukanlah 'jenis' itu sendiri, tetapi memiliki jenis* -> Constraint
. Saya merasa sulit untuk menghubungkan ide Haskell tentang 'jenis' (yang saya anggap terkait erat dengan jenis teori jenis?) Dengan contoh yang Anda berikan.:kind
perintah memberikan jenisNum
sebagai* -> Constraint
. Itu bisa spesifik untuk GHC, saya tidak tahu.