Saya ingin mengusulkan pendekatan yang lebih sistematis untuk menjawab pertanyaan ini, dan juga untuk menunjukkan contoh-contoh yang tidak menggunakan trik khusus seperti nilai "bawah" atau tipe data yang tak terbatas atau semacamnya.
Kapan konstruktor tipe gagal memiliki instance kelas tipe?
Secara umum, ada dua alasan mengapa konstruktor tipe bisa gagal memiliki turunan dari kelas tipe tertentu:
- Tidak dapat mengimplementasikan tanda tangan jenis metode yang diperlukan dari kelas tipe.
- Dapat menerapkan tanda tangan jenis tetapi tidak dapat memenuhi hukum yang disyaratkan.
Contoh jenis pertama lebih mudah daripada jenis kedua karena untuk jenis pertama, kita hanya perlu memeriksa apakah seseorang dapat mengimplementasikan suatu fungsi dengan tipe tanda tangan yang diberikan, sedangkan untuk jenis kedua, kita diharuskan membuktikan bahwa tidak ada implementasi mungkin bisa memenuhi hukum.
Contoh spesifik
Ini adalah contrafunctortor, bukan functor, sehubungan dengan parameter tipe a, karena adalam posisi contravarian. Tidak mungkin untuk mengimplementasikan fungsi dengan tipe tanda tangan (a -> b) -> F z a -> F z b.
Tipe konstruktor yang bukan merupakan fungsi yang sah meskipun tipe tanda tangan fmapdapat diimplementasikan:
data Q a = Q(a -> Int, a)
fmap :: (a -> b) -> Q a -> Q b
fmap f (Q(g, x)) = Q(\_ -> g x, f x) -- this fails the functor laws!
Aspek yang menarik dari contoh ini adalah bahwa kita dapat mengimplementasikan fmaptipe yang benar walaupun Ftidak mungkin menjadi functor karena digunakan adalam posisi contravarian. Jadi implementasi yang fmapditunjukkan di atas ini menyesatkan - meskipun memiliki tanda tangan jenis yang benar (saya percaya ini adalah satu-satunya implementasi yang mungkin dari tanda tangan jenis itu), undang-undang functor tidak puas. Misalnya, fmap id≠ id, karena let (Q(f,_)) = fmap id (Q(read,"123")) in f "456"memang 123, tetapi let (Q(f,_)) = id (Q(read,"123")) in f "456"ada 456.
Faktanya, Fini hanya seorang profunctor, - ia bukan functor atau contrafunctor.
Functor sah yang tidak berlaku karena jenis tanda tangan puretidak dapat diterapkan: mengambil monad Penulis (a, w)dan menghapus kendala yang wseharusnya menjadi monoid. Maka tidak mungkin untuk membangun nilai tipe (a, w)out a.
Sebuah functor yang tidak aplikatif karena jenis tanda tangan dari <*>kaleng tidak dilaksanakan: data F a = Either (Int -> a) (String -> a).
Sebuah functor yang tidak sah secara hukum meskipun metode tipe kelas dapat diimplementasikan:
data P a = P ((a -> Int) -> Maybe a)
Tipe konstruktor Padalah functor karena ahanya digunakan pada posisi kovarian.
instance Functor P where
fmap :: (a -> b) -> P a -> P b
fmap fab (P pa) = P (\q -> fmap fab $ pa (q . fab))
Satu-satunya implementasi yang mungkin dari tipe tanda tangan <*>adalah fungsi yang selalu mengembalikan Nothing:
(<*>) :: P (a -> b) -> P a -> P b
(P pfab) <*> (P pa) = \_ -> Nothing -- fails the laws!
Tetapi implementasi ini tidak memenuhi hukum identitas untuk fungsi aplikator.
- Sebuah functor yang
ApplicativebukanMonad karena tanda tangan jenis bindtidak dapat diimplementasikan.
Saya tidak tahu contoh seperti itu!
- Sebuah functor yang
Applicativetetapi bukanMonad karena hukum tidak dapat dipenuhi meskipun jenis tanda tangan binddapat diterapkan.
Contoh ini telah menghasilkan sedikit diskusi, jadi aman untuk mengatakan bahwa membuktikan contoh ini dengan benar tidak mudah. Tetapi beberapa orang telah memverifikasi ini secara independen dengan metode yang berbeda. Lihat Apakah `data PoE a = Kosong | Pasangkan aa a monad? untuk diskusi tambahan.
data B a = Maybe (a, a)
deriving Functor
instance Applicative B where
pure x = Just (x, x)
b1 <*> b2 = case (b1, b2) of
(Just (x1, y1), Just (x2, y2)) -> Just((x1, x2), (y1, y2))
_ -> Nothing
Agak sulit untuk membuktikan bahwa tidak ada Monadcontoh yang sah . Alasan untuk perilaku non-monadik adalah bahwa tidak ada cara alami untuk mengimplementasikan bindketika suatu fungsi f :: a -> B bdapat kembali Nothingatau Justuntuk nilai yang berbeda a.
Mungkin lebih jelas untuk mempertimbangkan Maybe (a, a, a), yang juga bukan monad, dan mencoba menerapkannya join. Seseorang akan menemukan bahwa tidak ada cara penerapan yang masuk akal secara intuitif join.
join :: Maybe (Maybe (a, a, a), Maybe (a, a, a), Maybe (a, a, a)) -> Maybe (a, a, a)
join Nothing = Nothing
join Just (Nothing, Just (x1,x2,x3), Just (y1,y2,y3)) = ???
join Just (Just (x1,x2,x3), Nothing, Just (y1,y2,y3)) = ???
-- etc.
Dalam kasus yang ditunjukkan oleh ???, tampak jelas bahwa kita tidak dapat memproduksi Just (z1, z2, z3)dengan cara yang masuk akal dan simetris dari enam nilai tipe yang berbeda a. Kita tentu saja dapat memilih subset arbitrer dari enam nilai ini, - misalnya, selalu mengambil nonempty pertama Maybe- tetapi ini tidak akan memenuhi hukum monad. Kembali Nothingjuga tidak akan memenuhi hukum.
- Struktur data seperti pohon yang bukan monad meskipun memiliki asosiatifitas untuk
bind- tetapi gagal dalam hukum identitas.
Monad seperti pohon (atau "pohon dengan cabang-cabang berbentuk fungsi") didefinisikan sebagai
data Tr f a = Leaf a | Branch (f (Tr f a))
Ini adalah monad gratis di atas functor f. Bentuk data adalah pohon di mana setiap titik cabang adalah "functor-ful" dari sub pohon. Pohon biner standar akan diperoleh dengan type f a = (a, a).
Jika kita memodifikasi struktur data ini dengan membuat juga daun dalam bentuk functor f, kita memperoleh apa yang saya sebut "semimonad" - ia memiliki bindyang memenuhi hukum naturalitas dan asosiasi, tetapi puremetodenya gagal dalam salah satu hukum identitas. "Semimonad adalah semigroup dalam kategori endofunctor, apa masalahnya?" Ini adalah kelas tipe Bind.
Untuk mempermudah, saya mendefinisikan joinmetode alih-alih bind:
data Trs f a = Leaf (f a) | Branch (f (Trs f a))
join :: Trs f (Trs f a) -> Trs f a
join (Leaf ftrs) = Branch ftrs
join (Branch ftrstrs) = Branch (fmap @f join ftrstrs)
Pencangkokan cabang adalah standar, tetapi pencangkokan daun adalah non-standar dan menghasilkan a Branch. Ini bukan masalah bagi hukum asosiatif tetapi melanggar salah satu hukum identitas.
Kapan tipe polinom memiliki turunan monad?
Tak satu pun dari functors Maybe (a, a)dan Maybe (a, a, a)dapat diberikan Monadcontoh yang sah , meskipun mereka jelas Applicative.
Fungsi-fungsi ini tidak memiliki trik - tidak ada Voidatau di bottommana pun, tidak ada kemalasan / keketatan yang rumit, tidak ada struktur tanpa batas, dan tidak ada batasan kelas tipe. Mesin Applicativevirtual benar-benar standar. Fungsi returndan binddapat diterapkan untuk fungsi-fungsi ini tetapi tidak akan memenuhi hukum monad. Dengan kata lain, fungsi-fungsi ini bukan monad karena struktur spesifik tidak ada (tetapi tidak mudah untuk memahami apa yang sebenarnya hilang). Sebagai contoh, perubahan kecil pada functor dapat membuatnya menjadi monad: data Maybe a = Nothing | Just aadalah monad. Functor serupa lainnya data P12 a = Either a (a, a)juga monad.
Konstruksi untuk polinomial monad
Secara umum, berikut adalah beberapa konstruksi yang menghasilkan Monadjenis polinomial yang sah menurut hukum . Dalam semua konstruksi ini, Madalah monad:
type M a = Either c (w, a)di mana wada monoid
type M a = m (Either c (w, a))di mana mada monad dan wmonoid
type M a = (m1 a, m2 a)di mana m1dan m2apa pun monad
type M a = Either a (m a)dimanakah mmonad?
Konstruksi pertama adalah WriterT w (Either c), konstruksi kedua adalah WriterT w (EitherT c m). Konstruksi ketiga adalah produk bijak komponen: pure @Mdidefinisikan sebagai produk bijak komponen pure @m1dan pure @m2, dan join @Mdidefinisikan dengan menghilangkan data produk-silang (misalnya m1 (m1 a, m2 a)dipetakan m1 (m1 a)dengan menghilangkan bagian kedua tupel):
join :: (m1 (m1 a, m2 a), m2 (m1 a, m2 a)) -> (m1 a, m2 a)
join (m1x, m2x) = (join @m1 (fmap fst m1x), join @m2 (fmap snd m2x))
Konstruksi keempat didefinisikan sebagai
data M m a = Either a (m a)
instance Monad m => Monad M m where
pure x = Left x
join :: Either (M m a) (m (M m a)) -> M m a
join (Left mma) = mma
join (Right me) = Right $ join @m $ fmap @m squash me where
squash :: M m a -> m a
squash (Left x) = pure @m x
squash (Right ma) = ma
Saya telah memeriksa bahwa keempat konstruksi menghasilkan monad yang sah.
Saya menduga bahwa tidak ada konstruksi lain untuk monad polinomial. Sebagai contoh, functor Maybe (Either (a, a) (a, a, a, a))tidak diperoleh melalui konstruksi ini dan tidak monadik. Namun, Either (a, a) (a, a, a)ini monadic karena isomorfik untuk produk dari tiga monads a, adan Maybe a. Juga, Either (a,a) (a,a,a,a)bersifat monadik karena isomorfik dengan produka dan Either a (a, a, a).
Keempat konstruksi yang ditunjukkan di atas akan memungkinkan kami untuk mendapatkan sejumlah produk dalam jumlah berapa pun a, misalnya Either (Either (a, a) (a, a, a, a)) (a, a, a, a, a))dan sebagainya. Semua konstruktor tipe seperti itu akan memiliki (setidaknya satu) Monadinstance.
Masih harus dilihat, kasus penggunaan apa yang mungkin ada untuk monad semacam itu. Masalah lain adalah bahwa Monadturunan yang diturunkan melalui konstruksi 1-4 pada umumnya tidak unik. Sebagai contoh, konstruktor tipe type F a = Either a (a, a)dapat diberikan Monadcontoh dalam dua cara: dengan konstruksi 4 menggunakan monad(a, a) , dan dengan konstruksi 3 menggunakan tipe isomorfisma Either a (a, a) = (a, Maybe a). Sekali lagi, menemukan kasus penggunaan untuk implementasi ini tidak segera jelas.
Sebuah pertanyaan tetap - diberikan tipe data polinomial sewenang-wenang, bagaimana mengenali apakah ia memiliki Monadinstance. Saya tidak tahu bagaimana membuktikan bahwa tidak ada konstruksi lain untuk polinomial monad. Saya tidak berpikir ada teori sejauh ini untuk menjawab pertanyaan ini.
* -> *) yang terdapat ada yang cocokfmap?