Saya belajar bahasa pemrograman Haskell, dan saya mencoba untuk membungkus kepala saya di sekitar apa perbedaan antara a type
dan a kind
.
Seperti yang saya pahami a kind is a type of type
,. Misalnya, a ford is a type of car
dan a car is a kind of vehicle
.
Apakah ini cara yang baik untuk memikirkan hal ini?
Karena, cara otak saya saat ini terhubung ford is a **type** of car
, tetapi juga car is a **type** of vehicle
sementara pada saat yang sama a car is a **kind** of vehicle
. Yaitu persyaratan type
dan kind
dapat dipertukarkan.
Adakah yang bisa menjelaskan hal ini?
type-theory
Thomas Cook
sumber
sumber
Jawaban:
Di sini, "nilai", "tipe", dan "jenis" memiliki arti formal, jadi mempertimbangkan penggunaan umum dalam bahasa Inggris atau analogi untuk mengklasifikasikan mobil hanya akan membuat Anda sejauh ini.
Jawaban saya berkaitan dengan makna formal dari istilah-istilah ini dalam konteks Haskell secara khusus; makna ini didasarkan pada (meskipun tidak benar-benar identik dengan) makna yang digunakan dalam matematika / CS "teori tipe". Jadi, ini bukan jawaban "ilmu komputer" yang sangat bagus, tetapi seharusnya berfungsi sebagai jawaban Haskell yang cukup bagus.
Dalam Haskell (dan bahasa lainnya), pada akhirnya membantu untuk menetapkan jenis ekspresi program yang menjelaskan kelas nilai yang diizinkan dimiliki oleh ekspresi tersebut. Saya berasumsi di sini bahwa Anda telah melihat cukup banyak contoh untuk memahami mengapa akan berguna untuk mengetahui bahwa dalam ekspresi
sqrt (a**2 + b**2)
, variabela
danb
akan selalu menjadi nilai tipeDouble
dan tidak, katakanlah,String
danBool
masing - masing. Pada dasarnya, memiliki tipe membantu kita dalam menulis ekspresi / program yang akan bekerja dengan benar pada berbagai nilai .Sekarang, sesuatu yang mungkin tidak Anda sadari adalah bahwa tipe Haskell, seperti yang muncul dalam tanda tangan tipe:
sebenarnya sendiri ditulis dalam sub-bahasa Haskell tipe-level. Teks program
Functor f => (a -> b) -> f a -> f b
adalah - secara harfiah - ekspresi tipe yang ditulis dalam subbahasa ini. Sub-bahasa termasuk operator (misalnya,->
adalah operator infiks asosiatif yang tepat dalam bahasa ini), variabel (misalnya,f
,a
, danb
), dan "aplikasi" dari satu jenis ekspresi yang lain (misalnya,f a
yangf
diterapkan untuka
).Apakah saya menyebutkan bagaimana membantu dalam banyak bahasa untuk menetapkan tipe ekspresi program untuk menggambarkan kelas nilai ekspresi? Nah, dalam subtitle bahasa tingkat-jenis ini, ekspresi mengevaluasi ke tipe (bukan nilai ) dan pada akhirnya membantu untuk menetapkan jenis untuk mengetik ekspresi untuk menggambarkan kelas tipe yang diizinkan untuk mereka wakili. Pada dasarnya, memiliki jenis membantu kita dalam menulis ekspresi tipe yang akan bekerja dengan benar pada berbagai jenis .
Jadi, nilai adalah tipe karena tipe adalah jenis , dan tipe membantu kita menulis program tingkat- nilai sementara jenis membantu kita menulis program tipe- tingkat.
Apa ini jenis terlihat seperti? Nah, pertimbangkan jenis tanda tangan:
Jika ekspresi jenis
a -> a
adalah sah, apa jenis dari jenis yang harus kita mengizinkan variabela
untuk menjadi? Nah, jenis ekspresi:terlihat valid, jadi tipe
Int
danBool
jelas dari jenis yang tepat . Tetapi jenis yang lebih rumit seperti:terlihat valid Bahkan, karena kita harus dapat memanggil
id
fungsi, bahkan:terlihat baik-baik saja. Jadi,
Int
,Bool
,[Double]
,Maybe [(Double,Int)]
, dana -> a
semua terlihat seperti jenis hak jenis .Dengan kata lain, sepertinya hanya ada satu jenis , sebut saja
*
seperti wildcard Unix, dan setiap jenis memiliki jenis yang sama*
, akhir cerita.Kanan?
Ya tidak cukup. Ternyata
Maybe
, dengan sendirinya, adalah ekspresi tipe yang validMaybe Int
(dalam banyak hal yang samasqrt
, semua dengan sendirinya, sama validnya dengan ekspresi nilaisqrt 25
). Namun , ekspresi tipe berikut ini tidak valid:Karena, sementara
Maybe
adalah ekspresi jenis, itu tidak mewakili jenis dari jenis yang dapat memiliki nilai-nilai. Jadi, itulah bagaimana kita harus mendefinisikan*
- itu adalah jenis dari jenis yang memiliki nilai-nilai; itu termasuk tipe "lengkap" sepertiDouble
atauMaybe [(Double,Int)]
tetapi tidak termasuk tipe tidak lengkap dan tidak berharga sepertiEither String
. Untuk kesederhanaan, saya akan menyebut tipe lengkap ini sebagai*
"tipe konkret", meskipun terminologi ini tidak universal, dan "tipe konkret" mungkin berarti sesuatu yang sangat berbeda dengan, katakanlah, seorang programmer C ++.Sekarang, dalam ekspresi jenis
a -> a
, selama jenisa
memiliki jenis*
(jenis jenis beton), hasil dari ekspresi jenis jugaa -> a
akan memiliki jenis (yaitu, jenis jenis beton).*
Jadi, apa jenis dari jenis ini
Maybe
? Nah,Maybe
bisa diterapkan pada jenis beton untuk menghasilkan jenis beton lain. Jadi,Maybe
tampak seperti sedikit seperti fungsi jenis-tingkat yang mengambil jenis dari jenis*
dan mengembalikan jenis dari jenis*
. Jika kita memiliki fungsi tingkat nilai yang mengambil nilai dari jenisInt
dan kembali nilai dari jenisInt
, kami akan memberikan jenis tanda tanganInt -> Int
, sehingga dengan analogi kita harus memberikanMaybe
sebuah jenis tanda tangan* -> *
. GHCi setuju:Kembali ke:
Dalam jenis tanda tangan ini, variabel
f
memiliki jenis* -> *
dan variabela
danb
jenis*
; operator bawaan->
memiliki jenis* -> * -> *
(dibutuhkan jenis*
di sebelah kiri dan satu di kanan dan mengembalikan jenis juga*
). Dari ini dan aturan inferensi jenis, Anda dapat menyimpulkan bahwa itua -> b
adalah jenis yang valid dengan jenis*
,f a
danf b
juga jenis yang valid dengan jenis*
, dan(a -> b) -> f a -> f b
jenis jenis yang valid*
.Dengan kata lain, kompiler dapat "jenis memeriksa" ekspresi tipe
(a -> b) -> f a -> f b
untuk memverifikasi itu valid untuk jenis variabel dari jenis yang tepat dengan cara yang sama "ketik memeriksa"sqrt (a**2 + b**2)
untuk memverifikasi itu valid untuk variabel dari jenis yang tepat.Alasan untuk menggunakan istilah terpisah untuk "jenis" versus "jenis" (yaitu, tidak berbicara tentang "jenis jenis") sebagian besar hanya untuk menghindari kebingungan. The jenis di atas tampilan yang sangat berbeda dari jenis dan, setidaknya pada awalnya, tampaknya berperilaku cukup berbeda. (Sebagai contoh, dibutuhkan beberapa waktu untuk membungkus kepala Anda sekitar gagasan bahwa setiap "normal" jenis memiliki jenis yang sama
*
dan jenisa -> b
yang*
tidak* -> *
.)Beberapa di antaranya juga historis. Ketika GHC Haskell telah berevolusi, perbedaan antara nilai, tipe, dan jenis sudah mulai kabur. Hari-hari ini, nilai-nilai dapat "dipromosikan" ke dalam tipe, dan tipe dan jenis adalah hal yang sama. Jadi, di Haskell modern, nilai keduanya memiliki tipe dan tipe ARE (hampir), dan jenis-jenisnya hanyalah tipe yang lebih banyak.
@ user21820 meminta beberapa penjelasan tambahan tentang "jenis dan jenisnya adalah hal yang sama". Untuk menjadi sedikit lebih jelas, dalam GHC Haskell modern (sejak versi 8.0.1, saya pikir), jenis dan jenis diperlakukan secara seragam di sebagian besar kode kompiler. Kompiler melakukan upaya dalam pesan kesalahan untuk membedakan antara "jenis" dan "jenis", tergantung pada apakah itu mengeluh tentang jenis nilai atau jenis jenis, masing-masing.
Juga, jika tidak ada ekstensi yang diaktifkan, ekstensi tersebut mudah dibedakan dalam bahasa permukaan. Sebagai contoh, tipe (nilai) memiliki representasi dalam sintaks (misalnya, dalam tipe tanda tangan), tetapi jenis (tipe) adalah - saya pikir - sepenuhnya tersirat, dan tidak ada sintaksis eksplisit di mana mereka muncul.
Tetapi, jika Anda mengaktifkan ekstensi yang sesuai, perbedaan antara jenis dan jenis sebagian besar menghilang. Sebagai contoh:
Di sini,
Bar
adalah (baik nilai dan) tipe. Sebagai jenis, jenisnya adalahBool -> * -> Foo
, yang merupakan fungsi tipe-tingkat yang mengambil jenis jenisBool
(yang merupakan jenis, tetapi juga jenis) dan jenis jenis*
dan menghasilkan jenis jenisFoo
. Begitu:memeriksa dengan benar.
Sebagai @AndrejBauer menjelaskan dalam jawabannya, kegagalan untuk membedakan antara jenis dan jenis tidak aman - memiliki jenis / jenis
*
yang jenis / jenisnya itu sendiri (yang merupakan kasus di Haskell modern) mengarah ke paradoks. Namun, sistem tipe Haskell sudah penuh dengan paradoks karena non-terminasi, sehingga tidak dianggap sebagai masalah besar.sumber
type
hanyalahtype
dirinya sendiri, dan tidak perlu sama sekalikind
. Jadi apa tepatnya perbedaannya?Bool
adalah tipeType
adalah sejenis karena unsur-unsurnya adalah jenisBool -> Int
adalah tipeBool -> Type
adalah sejenis karena elemen-elemennya adalah fungsi yang mengembalikan tipeBool * Int
adalah tipeBool * Type
adalah sejenis karena unsur-unsurnya berpasangan dengan satu komponen jenis*
*
U_1
sumber
Type :: Type
adalah sebuah aksioma. Perbedaan antara "tipe" dan "jenis" sepenuhnya dalam bahasa manusia, dalam hal ini.True
memiliki tipe,,Bool
danBool
memiliki tipeType
, yang tipe itu sendiriType
. Kadang-kadang kita menyebut jenis tipe, untuk menekankan bahwa itu adalah tipe entitas tipe-level, tetapi, di Haskell, itu masih hanya tipe. Dalam sebuah sistem di mana alam semesta benar-benar ada, seperti Coq, maka "tipe" dapat merujuk ke satu alam semesta dan "jenis" yang lain, tetapi kemudian kita biasanya menginginkan banyak alam semesta yang tak terhingga banyaknya.Type :: Type
dan perbedaan antara jenis dan jenis. Selain itu, potongan kode apa yang diperagakanType :: Type
di Haskell?*
di Haskell adalah semacam semesta. Mereka hanya tidak menyebutnya begitu.Type
dariData.Kinds
dan*
harus sinonim. Awalnya kami hanya memiliki*
sebagai primitif, sementara saat ini yang didefinisikanGHC.Types.Type
secara internal dalam modul internalGHC.Types
, pada gilirannya didefinisikan sebagaitype Type = TYPE LiftedRep
. Saya pikirTYPE
adalah primitif nyata, menyediakan keluarga jenis (tipe lift, tipe unboxed, ...). Sebagian besar kompleksitas "inelegant" di sini adalah untuk mendukung beberapa optimasi tingkat rendah, dan bukan untuk alasan teoretik tipe aktual.v
adalah nilai, maka ia memiliki tipe:v :: T
. JikaT
adalah tipe, maka ia memiliki tipe:T :: K
. Jenis jenis ini disebut jenisnya. Jenis yang terlihat sepertiTYPE rep
bisa disebut macam, meskipun kata itu tidak umum. IFFT :: TYPE rep
adalahT
diizinkan untuk tampil di RHS dari::
. Kata "jenis" memiliki nuansa padanya:K
inT :: K
adalah sejenis, tetapi tidak dalamv :: K
, meskipun samaK
. Kita dapat mendefinisikan "K
adalah jenis jika jenisnya adalah jenis" alias "jenisnya ada di RHS::
", tetapi itu tidak menangkap penggunaannya dengan benar. Karena itu posisi "perbedaan manusia" saya.Sebuah nilai adalah seperti spesifik merah 2011 Ford Mustang dengan 19.206 mil di atasnya bahwa Anda telah duduk di halaman Anda.
Nilai spesifik itu, secara informal, dapat memiliki banyak jenis : itu adalah Mustang, dan itu adalah Ford, dan itu adalah sebuah mobil, dan itu adalah sebuah kendaraan, di antara banyak jenis lainnya yang dapat Anda hasilkan (jenis "barang" milik Anda ", atau jenis" hal-hal yang berwarna merah ", atau ...).
(Di Haskell, ke perkiraan orde pertama (GADT menghancurkan properti ini, dan keajaiban di sekitar literal angka dan ekstensi OverloadedStrings mengaburkan sedikit), nilai memiliki satu jenis utama alih-alih kebanyakan "jenis" informal yang dapat Anda berikan kepada Anda ' stang.
42
, adalah untuk tujuan penjelasan ini, sebuahInt
; tidak ada jenis dalam Haskell untuk "angka" atau "bahkan bilangan bulat" —atau lebih tepatnya, Anda bisa membuatnya, tetapi itu akan menjadi tipe terpisah dariInt
.)Sekarang, "Mustang" mungkin merupakan subtipe dari "mobil" - setiap nilai yang merupakan Mustang juga merupakan mobil. Tetapi tipe —atau, untuk menggunakan terminologi Haskell, jenis "Mustang" bukanlah "mobil". "Mustang" jenisnya bukanlah sesuatu yang bisa Anda parkirkan di jalan masuk atau berkeliling. "Mustang" adalah kata benda, atau kategori, atau hanya tipe. Itu adalah, secara informal, jenis - jenis "Mustang".
(Lagi-lagi, Haskell hanya mengenali satu jenis untuk setiap hal level-jenis. Begitu
Int
juga jenis*
, dan tidak ada jenis lain,Maybe
memiliki jenis* -> *
, dan tidak ada jenis lain. Tetapi intuisi masih harus dipegang:42
adalahInt
, dan Anda dapat melakukanInt
hal-hal dengan itu. seperti menambah dan mengurangi,Int
itu sendiri bukan suatuInt
; tidak ada angka seperti ituInt + Int
. Anda dapat secara informal mendengar orang mengatakan bahwa ituInt
adalahNum
, yang berarti bahwa ada turunan dariNum
tipe kelas untuk tipeInt
— ini bukan hal yang sama mengatakan bahwa yangInt
memiliki jenisNum
.Int
memiliki jenis "jenis", yang dalam Haskell dieja*
.)Jadi bukankah setiap "tipe" informal hanya berupa kata benda atau kategori? Apakah semua jenis memiliki jenis yang sama? Mengapa berbicara tentang jenis sama sekali jika mereka sangat membosankan?
Di sinilah analogi bahasa Inggris akan menjadi sedikit berbatu, tetapi bersabarlah: berpura-pura bahwa kata "pemilik" dalam bahasa Inggris tidak masuk akal secara terpisah, tanpa uraian tentang benda yang dimiliki. Berpura-puralah bahwa jika seseorang menyebut Anda "pemilik", itu sama sekali tidak masuk akal bagi Anda; tetapi jika seseorang memanggil Anda "pemilik mobil", Anda bisa mengerti apa artinya.
"Pemilik" tidak memiliki jenis yang sama dengan "mobil", karena Anda dapat berbicara tentang mobil, tetapi Anda tidak dapat berbicara tentang pemilik dalam versi bahasa Inggris yang dibuat-buat ini. Anda hanya dapat berbicara tentang "pemilik mobil". "Pemilik" hanya menciptakan sesuatu yang seperti "kata benda" ketika diterapkan pada sesuatu yang sudah memiliki "kata benda", seperti "mobil". Kami akan mengatakan bahwa jenis "pemilik" adalah "kata benda -> kata benda". "Pemilik" seperti fungsi yang mengambil kata benda dan menghasilkan dari kata benda yang berbeda; tapi itu bukan kata benda itu sendiri.
Perhatikan bahwa "pemilik mobil" bukan subtipe "mobil"! Ini bukan fungsi yang menerima atau mengembalikan mobil! Itu hanya tipe yang sepenuhnya terpisah dari "mobil". Ini menggambarkan nilai-nilai dengan dua tangan dan dua kaki yang pada satu titik memiliki sejumlah uang tertentu, dan membawa uang itu ke dealer. Itu tidak menggambarkan nilai-nilai yang memiliki empat roda dan pekerjaan cat. Juga perhatikan bahwa "pemilik mobil" dan "pemilik anjing" adalah tipe yang berbeda, dan hal-hal yang mungkin ingin Anda lakukan dengan satu mungkin tidak berlaku untuk yang lain.
(Demikian juga, ketika kita mengatakan bahwa
Maybe
memiliki jenis* -> *
Haskell, yang kita maksud adalah tidak masuk akal (secara formal; secara informal kita melakukannya sepanjang waktu) untuk berbicara tentang memiliki "aMaybe
". Sebaliknya, kita dapat memilikiMaybe Int
atauMaybe String
, karena itu adalah hal-hal dari baik*
.)Jadi inti dari berbicara tentang jenis sama sekali adalah agar kita dapat memformalisasikan penalaran kita di sekitar kata-kata seperti "pemilik" dan menegakkan bahwa kita hanya pernah mengambil nilai-nilai jenis yang telah "sepenuhnya dibangun" dan tidak masuk akal.
sumber
Itu benar - jadi mari kita jelajahi apa artinya itu.
Int
atauText
tipe konkret, tetapiMaybe a
merupakan tipe abstrak . Itu tidak akan menjadi tipe konkret sampai Anda memutuskan nilai spesifik apa yang Anda inginkan untuka
variabel tertentu (atau nilai / ekspresi / apa pun) misalnyaMaybe Text
.Kami mengatakan bahwa itu
Maybe a
adalah konstruktor tipe karena itu seperti fungsi yang mengambil tipe beton tunggal (misalnyaText
) dan mengembalikan tipe beton (Maybe Text
dalam hal ini). Tetapi konstruktor tipe lain mungkin mengambil lebih banyak "params input" sebelum mereka mengembalikan tipe konkret. mis.Map k v
perlu mengambil dua jenis beton (misalnyaInt
danText
) sebelum dapat membangun jenis beton (Map Int Text
).Jadi, konstruktor
Maybe a
danList a
ketik memiliki "tanda tangan" yang sama dengan yang kami tunjukkan* -> *
(mirip dengan tanda tangan fungsi Haskell) karena jika Anda memberi mereka satu jenis beton, mereka akan memuntahkan jenis beton. Kami menyebutnya "jenis" dari jenis danMaybe
danList
memiliki jenis yang sama.Jenis beton dikatakan memiliki jenis
*
, dan contoh Peta kami adalah jenis* -> * -> *
karena dibutuhkan dua jenis beton sebagai input sebelum dapat menghasilkan jenis beton.Anda dapat melihatnya sebagian besar hanya tentang jumlah "parameter" yang kami sampaikan ke konstruktor tipe - tetapi menyadari bahwa kami mungkin juga mendapatkan konstruktor tipe yang bersarang di dalam konstruktor tipe, sehingga kami dapat berakhir dengan jenis yang terlihat seperti
* -> (* -> *) -> *
misalnya .Jika Anda seorang Scala / pengembang Java, Anda juga dapat menemukan penjelasan ini bermanfaat: https://www.atlassian.com/blog/archives/scala-types-of-a-higher-kind
sumber
Maybe a
, sinonim untukforall a. Maybe a
, jenis jenis polimorfik*
, danMaybe
, jenis jenis monomorfik* -> *
.