Saya mencoba membandingkan kelas tipe Haskell dan antarmuka C #. Misalkan ada a Functor
.
Haskell:
class Functor f where
fmap :: (a -> b) -> f a -> f b
Bagaimana menerapkan kelas tipe ini sebagai antarmuka dalam C #?
Apa yang saya coba:
interface Functor<A, B>
{
F<B> fmap(Func<A, B> f, F<A> x);
}
Ini adalah implementasi yang tidak valid dan saya sebenarnya terjebak dengan F
tipe generik yang harus dikembalikan oleh fmap
. Bagaimana itu harus didefinisikan dan dimana?
Apakah tidak mungkin diimplementasikan Functor
dalam C # dan mengapa? Atau mungkin ada pendekatan lain?
Jawaban:
Sistem tipe C # tidak memiliki beberapa fitur yang diperlukan untuk mengimplementasikan kelas tipe dengan benar sebagai antarmuka.
Mari kita mulai dengan contoh Anda, tetapi kuncinya menunjukkan akun yang lebih lengkap tentang apa itu typeclass dan apa yang dilakukan, dan kemudian mencoba memetakannya ke bit C #.
Ini adalah definisi kelas tipe, atau mirip dengan antarmuka. Sekarang mari kita lihat definisi dari suatu tipe dan implementasi dari kelas tipe itu.
Sekarang kita bisa melihat dengan jelas satu fakta berbeda dari kelas tipe yang tidak bisa Anda lakukan miliki dengan antarmuka. Implementasi kelas tipe bukan bagian dari definisi tipe. Di C #, untuk mengimplementasikan antarmuka, Anda harus mengimplementasikannya sebagai bagian dari definisi tipe yang mengimplementasikannya. Ini berarti Anda tidak dapat mengimplementasikan antarmuka untuk jenis yang tidak Anda laksanakan sendiri, namun di Haskell Anda dapat menerapkan kelas jenis untuk jenis apa pun yang Anda akses.
Itu mungkin yang terbesar dengan segera, tetapi ada perbedaan lain yang cukup signifikan yang membuat padanan C # benar-benar tidak berfungsi, dan Anda menyentuhnya dalam pertanyaan Anda. Ini tentang Polimorfisme. Juga ada beberapa hal yang relatif umum yang Haskell memungkinkan Anda lakukan dengan kelas tipe yang langsung tidak menerjemahkan terutama ketika Anda mulai melihat jumlah genericism dalam tipe eksistensial atau ekstensi GHC lainnya seperti Generic ADTs.
Anda lihat, dengan Haskell Anda dapat mendefinisikan functors
Kemudian dalam konsumsi Anda dapat memiliki fungsi:
Di sinilah letak masalahnya. Dalam C # bagaimana Anda menulis fungsi ini?
Jadi ada beberapa hal yang salah dengan versi C #, untuk satu hal yang saya bahkan tidak yakin itu akan memungkinkan Anda untuk menggunakan
<b>
kualifikasi seperti saya lakukan di sana, tapi tanpa itu saya saya yakin itu tidak akan mengirimkanShow<>
tepat (merasa bebas untuk mencoba dan kompilasi untuk mencari tahu; saya tidak).Namun masalah yang lebih besar di sini adalah bahwa tidak seperti di atas di Haskell di mana kita memiliki
Terminal
definisi kita sebagai bagian dari tipe dan kemudian dapat digunakan sebagai pengganti tipe, karena C # kurang polimorfisme parametrik yang sesuai (yang menjadi sangat jelas segera setelah Anda mencoba untuk interop F # dengan C #) Anda tidak dapat dengan jelas atau bersih membedakan apakah Kanan atau Kiri adalahTerminal
s. Yang terbaik yang dapat Anda lakukan adalah menggunakannull
, tetapi bagaimana jika Anda mencoba membuat tipe nilai aFunctor
atau dalam kasus diEither
mana Anda membedakan dua tipe yang sama-sama membawa nilai? Sekarang Anda harus menggunakan satu jenis dan memiliki dua nilai yang berbeda untuk memeriksa dan beralih antara untuk memodelkan diskriminasi Anda?Kurangnya jumlah jumlah yang tepat, jenis serikat, ADT, apa pun yang Anda ingin menyebutnya benar-benar membuat banyak jenis kacamata memberi Anda murtad karena pada akhirnya mereka membiarkan Anda memperlakukan beberapa jenis (konstruktor) sebagai satu jenis, dan sistem tipe .NET yang mendasarinya tidak memiliki konsep seperti itu.
sumber
Yang Anda butuhkan adalah dua kelas, satu untuk memodelkan orde tinggi generik (functor) dan satu lagi untuk memodelkan functor gabungan dengan nilai bebas A
Jadi jika kita menggunakan Option monad (karena semua monad adalah functors)
Anda kemudian dapat menggunakan metode ekstensi statis untuk mengkonversi dari IF <Option, B> ke Some <A> saat Anda perlu
sumber
pure
antarmuka functor generik: kompiler mengeluh padaIF<Functor, A> pure<A>(A a);
dengan "TipeFunctor
tidak dapat digunakan sebagai tipe parameterFunctor
dalam tipe metode generikIF<Functor, A>
. Tidak ada konversi tinju atau konversi parameter tipe dariFunctor
keF<Functor>
." Apa artinya ini? Dan mengapa kita harus mendefinisikanpure
di dua tempat? Selain itu, tidakpure
boleh statis?