Apa perbedaan antara sifat-sifat di Rust dan typeclasses di Haskell?
157
Ciri-ciri di Rust tampaknya setidaknya mirip dengan jenis kacamata di Haskell, namun saya telah melihat orang menulis bahwa ada beberapa perbedaan di antara mereka. Saya bertanya-tanya apa perbedaan-perbedaan ini.
Saya tidak tahu banyak tentang Rust. Tetapi batu sandungan yang umum untuk teknologi serupa dalam bahasa lain adalah jenis yang lebih tinggi (misalnya, apakah ciri-ciri dapat berkisar pada jenis yang diparameterisasi, tetapi bukan parameternya?) Dan polimorfisme tipe-kembali (misalnya, apakah jenis sifat dapat muncul dalam hasil fungsi, tetapi tidak di mana pun dalam argumen?). Contoh dari mantan di Haskell adalah class Functor f where fmap :: (a -> b) -> (f a -> f b); contoh dari yang terakhir adalah class Bounded a where maxBound :: a.
Daniel Wagner
4
GHC juga mendukung kelas tipe multi-parameter (yaitu, sifat yang melibatkan beberapa jenis) dan dependensi fungsional, meskipun ini bukan bagian dari spesifikasi Haskell resmi. Menilai dari sintaks Rust yang disarankan di tautan Anda, itu hanya dapat mendukung sifat yang berkisar lebih dari satu jenis pada satu waktu, meskipun penilaian itu sekali lagi tidak didasarkan pada pengalaman yang mendalam.
Daniel Wagner
4
@DanielWagner Polimorfisme tipe-kembali ada (misalnya std::default), dan jenis pekerjaan multiparameter semacam (termasuk analog dependensi fungsional), meskipun AFAIK perlu bekerja di sekitar parameter pertama yang diistimewakan. Namun tidak ada HKT. Mereka berada di wishlist jauh masa depan tetapi belum di cakrawala.
4
Perbedaan lainnya adalah perawatan anak yatim. Rust mencoba untuk memiliki aturan koherensi yang lebih ketat tentang di mana alat baru untuk suatu sifat dapat ditulis. Lihat diskusi ini untuk lebih detail (khususnya di sini )
Pada tingkat dasar, tidak ada banyak perbedaan, tetapi mereka masih ada di sana.
Haskell menjelaskan fungsi atau nilai yang didefinisikan dalam typeclass sebagai 'metode', sama seperti sifat menggambarkan metode OOP dalam objek yang mereka sertakan. Namun, Haskell berurusan dengan ini secara berbeda, memperlakukan mereka sebagai nilai-nilai individual daripada menyematkannya ke objek karena OOP akan membuat orang melakukannya. Ini adalah tentang perbedaan level permukaan yang paling jelas.
Satu hal yang tidak bisa dilakukan Rust untuk sementara waktu adalah sifat-sifat yang diketik tingkat tinggi , seperti kacamata yang terkenal Functordan Monadburuk.
Ini berarti bahwa sifat-sifat Rust hanya bisa menggambarkan apa yang sering disebut 'tipe konkret', dengan kata lain, karakter tanpa argumen umum. Haskell sejak awal bisa membuat kacamata ketik tingkat tinggi yang menggunakan tipe yang mirip dengan bagaimana fungsi tingkat tinggi menggunakan fungsi lain: menggunakan satu untuk menggambarkan yang lain. Untuk periode waktu ini tidak mungkin di Rust, tetapi karena item terkait telah diterapkan, sifat-sifat tersebut telah menjadi biasa dan idiomatis.
Jadi jika kita mengabaikan ekstensi, mereka tidak persis sama, tetapi masing-masing dapat memperkirakan apa yang dapat dilakukan oleh yang lain.
Juga disebutkan, seperti dikatakan dalam komentar, bahwa GHC (kompilator utama Haskell) mendukung opsi lebih lanjut untuk typeclasses, termasuk typeclasses multi-parameter (yaitu banyak jenis yang terlibat), dan dependensi fungsional , pilihan yang bagus yang memungkinkan untuk perhitungan tingkat-jenis. , dan mengarah ke mengetik keluarga . Sepengetahuan saya, Rust tidak memiliki funDeps atau tipe keluarga, meskipun mungkin di masa depan. †
Semua dalam semua, ciri dan kacamata memiliki perbedaan mendasar, yang karena cara mereka berinteraksi, membuat mereka bertindak dan tampak sangat mirip pada akhirnya.
† Artikel bagus tentang kacamata Haskell (termasuk yang berjenis lebih tinggi) dapat ditemukan di sini , dan bab Karat dengan Contoh tentang sifat dapat ditemukan di sini
Karat masih belum memiliki bentuk jenis yang lebih tinggi. "Terkenal" membutuhkan pembenaran. Functor sangat luas dan berguna sebagai konsep. Tipe keluarga sama dengan tipe terkait. Ketergantungan fungsional pada dasarnya berlebihan dengan tipe terkait (termasuk di Haskell). Masalahnya Rust tidak memiliki wrt. fundeps adalah anotasi injeksi. Anda memilikinya mundur, sifat-sifat Rust dan kelas-kelas tipe Haskell berbeda di permukaan tetapi banyak perbedaan menguap ketika Anda melihat ke bawah. Perbedaan yang sebagian besar melekat pada domain yang berbeda di mana bahasa beroperasi.
Centril
Item terkait sekarang dianggap idomatik dalam banyak keadaan, bukan?
Vaelus
@Vaelus Anda benar — Jawaban ini harus sedikit diperbarui. Editing sekarang.
AJFarmar
19
Saya pikir jawaban saat ini mengabaikan perbedaan paling mendasar antara sifat-sifat Rust dan kelas tipe Haskell. Perbedaan-perbedaan ini berkaitan dengan cara sifat terkait dengan konstruksi bahasa berorientasi objek. Untuk informasi tentang ini, lihat buku Karat .
Deklarasi sifat menciptakan tipe sifat . Ini berarti bahwa Anda dapat mendeklarasikan variabel jenis tersebut (atau lebih tepatnya, referensi jenis tersebut). Anda juga dapat menggunakan tipe trait sebagai parameter pada fungsi, bidang struct, dan tipe instantiasi parameter.
Variabel referensi sifat dapat pada saat runtime berisi objek dari tipe yang berbeda, selama tipe runtime dari objek yang direferensikan mengimplementasikan sifat tersebut.
// The shape variable might contain a Square or a Circle, // we don't know until runtimelet shape:&Shape= get_unknown_shape();// Might contain different kinds of shapes at the same timelet shapes:Vec<&Shape>= get_shapes();
Ini bukan cara kerja kelas tipe. Tipe kelas tidak membuat tipe , jadi Anda tidak bisa mendeklarasikan variabel dengan nama kelas. Kelas tipe bertindak sebagai batas pada parameter tipe, tetapi parameter tipe harus dipakai dengan tipe beton, bukan kelas tipe itu sendiri.
Anda tidak dapat memiliki daftar hal-hal berbeda dari tipe berbeda yang mengimplementasikan kelas tipe yang sama. (Sebaliknya, tipe eksistensial digunakan dalam Haskell untuk mengekspresikan hal yang serupa.) Catatan 1
Metode sifat dapat dikirim secara dinamis . Ini sangat terkait dengan hal-hal yang dijelaskan pada bagian di atas.
Pengiriman dinamis berarti jenis runtime objek yang menjadi titik rujukan digunakan untuk menentukan metode mana yang disebut melalui referensi.
let shape:&Shape= get_unknown_shape();// This calls a method, which might be Square.area or// Circle.area depending on the runtime type of shapeprint!("Area: {}", shape.area());
Sekali lagi, tipe eksistensial digunakan untuk ini di Haskell.
Kesimpulannya
Sepertinya bagi saya sifat-sifat ada dalam banyak aspek konsep yang sama dengan kelas tipe. Selain itu, mereka memiliki fungsi antarmuka berorientasi objek.
Di sisi lain, kelas tipe Haskell lebih maju. Haskell misalnya memiliki tipe yang lebih tinggi dan ekstensi seperti kelas tipe multi-parameter.
Catatan 1 : Versi terbaru dari Rust memiliki pembaruan untuk membedakan penggunaan nama sifat sebagai tipe dan penggunaan nama sifat sebagai batas. Dalam jenis sifat nama diawali oleh dynkata kunci. Lihat misalnya jawaban ini untuk informasi lebih lanjut.
"Tipe kelas tidak membuat tipe" - Saya pikir yang terbaik adalah memahami dyn Traitsebagai bentuk pengetikan eksistensial karena mereka berhubungan dengan sifat / kelas tipe. Kita dapat mempertimbangkan dynoperator pada batas memproyeksikan mereka untuk jenis, yaitu dyn : List Bound -> Type. Mengambil ide ini untuk Haskell, dan berkaitan dengan "sehingga Anda tidak dapat mendeklarasikan variabel dengan nama kelas", kita bisa melakukan ini secara tidak langsung dalam Haskell: data Dyn (c :: * -> Constraint) = forall (t :: Type). c t => D t. Setelah mendefinisikan ini, kami dapat bekerja dengannya [D True, D "abc", D 42] :: [D Show].
Centril
8
"Sifat" Rust adalah analog dengan kelas tipe Haskell.
Perbedaan utama dengan Haskell adalah bahwa sifat hanya mengintervensi ekspresi dengan notasi titik, yaitu dari bentuk a.foo (b).
Kelas tipe Haskell meluas ke tipe tingkat tinggi. Hanya sifat-sifat karat yang tidak mendukung tipe urutan yang lebih tinggi karena mereka hilang dari keseluruhan bahasa, artinya bukan perbedaan filosofis antara sifat-sifat dan kelas tipe
Ciri-ciri di Rust tidak "hanya mengintervensi ekspresi dengan notasi titik". Sebagai contoh, pertimbangkanDefault sifat yang tidak memiliki metode, hanya fungsi terkait non-metode.
class Functor f where fmap :: (a -> b) -> (f a -> f b)
; contoh dari yang terakhir adalahclass Bounded a where maxBound :: a
.std::default
), dan jenis pekerjaan multiparameter semacam (termasuk analog dependensi fungsional), meskipun AFAIK perlu bekerja di sekitar parameter pertama yang diistimewakan. Namun tidak ada HKT. Mereka berada di wishlist jauh masa depan tetapi belum di cakrawala.Jawaban:
Pada tingkat dasar, tidak ada banyak perbedaan, tetapi mereka masih ada di sana.
Haskell menjelaskan fungsi atau nilai yang didefinisikan dalam typeclass sebagai 'metode', sama seperti sifat menggambarkan metode OOP dalam objek yang mereka sertakan. Namun, Haskell berurusan dengan ini secara berbeda, memperlakukan mereka sebagai nilai-nilai individual daripada menyematkannya ke objek karena OOP akan membuat orang melakukannya. Ini adalah tentang perbedaan level permukaan yang paling jelas.
Satu hal yang tidak bisa dilakukan Rust untuk sementara waktu adalah sifat-sifat yang diketik tingkat tinggi , seperti kacamata yang terkenal
Functor
danMonad
buruk.Ini berarti bahwa sifat-sifat Rust hanya bisa menggambarkan apa yang sering disebut 'tipe konkret', dengan kata lain, karakter tanpa argumen umum. Haskell sejak awal bisa membuat kacamata ketik tingkat tinggi yang menggunakan tipe yang mirip dengan bagaimana fungsi tingkat tinggi menggunakan fungsi lain: menggunakan satu untuk menggambarkan yang lain. Untuk periode waktu ini tidak mungkin di Rust, tetapi karena item terkait telah diterapkan, sifat-sifat tersebut telah menjadi biasa dan idiomatis.
Jadi jika kita mengabaikan ekstensi, mereka tidak persis sama, tetapi masing-masing dapat memperkirakan apa yang dapat dilakukan oleh yang lain.
Juga disebutkan, seperti dikatakan dalam komentar, bahwa GHC (kompilator utama Haskell) mendukung opsi lebih lanjut untuk typeclasses, termasuk typeclasses multi-parameter (yaitu banyak jenis yang terlibat), dan dependensi fungsional , pilihan yang bagus yang memungkinkan untuk perhitungan tingkat-jenis. , dan mengarah ke mengetik keluarga . Sepengetahuan saya, Rust tidak memiliki funDeps atau tipe keluarga, meskipun mungkin di masa depan. †
Semua dalam semua, ciri dan kacamata memiliki perbedaan mendasar, yang karena cara mereka berinteraksi, membuat mereka bertindak dan tampak sangat mirip pada akhirnya.
† Artikel bagus tentang kacamata Haskell (termasuk yang berjenis lebih tinggi) dapat ditemukan di sini , dan bab Karat dengan Contoh tentang sifat dapat ditemukan di sini
sumber
Saya pikir jawaban saat ini mengabaikan perbedaan paling mendasar antara sifat-sifat Rust dan kelas tipe Haskell. Perbedaan-perbedaan ini berkaitan dengan cara sifat terkait dengan konstruksi bahasa berorientasi objek. Untuk informasi tentang ini, lihat buku Karat .
Deklarasi sifat menciptakan tipe sifat . Ini berarti bahwa Anda dapat mendeklarasikan variabel jenis tersebut (atau lebih tepatnya, referensi jenis tersebut). Anda juga dapat menggunakan tipe trait sebagai parameter pada fungsi, bidang struct, dan tipe instantiasi parameter.
Variabel referensi sifat dapat pada saat runtime berisi objek dari tipe yang berbeda, selama tipe runtime dari objek yang direferensikan mengimplementasikan sifat tersebut.
Ini bukan cara kerja kelas tipe. Tipe kelas tidak membuat tipe , jadi Anda tidak bisa mendeklarasikan variabel dengan nama kelas. Kelas tipe bertindak sebagai batas pada parameter tipe, tetapi parameter tipe harus dipakai dengan tipe beton, bukan kelas tipe itu sendiri.
Anda tidak dapat memiliki daftar hal-hal berbeda dari tipe berbeda yang mengimplementasikan kelas tipe yang sama. (Sebaliknya, tipe eksistensial digunakan dalam Haskell untuk mengekspresikan hal yang serupa.) Catatan 1
Metode sifat dapat dikirim secara dinamis . Ini sangat terkait dengan hal-hal yang dijelaskan pada bagian di atas.
Pengiriman dinamis berarti jenis runtime objek yang menjadi titik rujukan digunakan untuk menentukan metode mana yang disebut melalui referensi.
Sekali lagi, tipe eksistensial digunakan untuk ini di Haskell.
Kesimpulannya
Sepertinya bagi saya sifat-sifat ada dalam banyak aspek konsep yang sama dengan kelas tipe. Selain itu, mereka memiliki fungsi antarmuka berorientasi objek.
Di sisi lain, kelas tipe Haskell lebih maju. Haskell misalnya memiliki tipe yang lebih tinggi dan ekstensi seperti kelas tipe multi-parameter.
Catatan 1 : Versi terbaru dari Rust memiliki pembaruan untuk membedakan penggunaan nama sifat sebagai tipe dan penggunaan nama sifat sebagai batas. Dalam jenis sifat nama diawali oleh
dyn
kata kunci. Lihat misalnya jawaban ini untuk informasi lebih lanjut.sumber
dyn Trait
sebagai bentuk pengetikan eksistensial karena mereka berhubungan dengan sifat / kelas tipe. Kita dapat mempertimbangkandyn
operator pada batas memproyeksikan mereka untuk jenis, yaitudyn : List Bound -> Type
. Mengambil ide ini untuk Haskell, dan berkaitan dengan "sehingga Anda tidak dapat mendeklarasikan variabel dengan nama kelas", kita bisa melakukan ini secara tidak langsung dalam Haskell:data Dyn (c :: * -> Constraint) = forall (t :: Type). c t => D t
. Setelah mendefinisikan ini, kami dapat bekerja dengannya[D True, D "abc", D 42] :: [D Show]
."Sifat" Rust adalah analog dengan kelas tipe Haskell.
Perbedaan utama dengan Haskell adalah bahwa sifat hanya mengintervensi ekspresi dengan notasi titik, yaitu dari bentuk a.foo (b).
Kelas tipe Haskell meluas ke tipe tingkat tinggi. Hanya sifat-sifat karat yang tidak mendukung tipe urutan yang lebih tinggi karena mereka hilang dari keseluruhan bahasa, artinya bukan perbedaan filosofis antara sifat-sifat dan kelas tipe
sumber
Default
sifat yang tidak memiliki metode, hanya fungsi terkait non-metode.