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 a
dalam 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 fmap
dapat 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 fmap
tipe yang benar walaupun F
tidak mungkin menjadi functor karena digunakan a
dalam posisi contravarian. Jadi implementasi yang fmap
ditunjukkan 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, F
ini hanya seorang profunctor, - ia bukan functor atau contrafunctor.
Functor sah yang tidak berlaku karena jenis tanda tangan pure
tidak dapat diterapkan: mengambil monad Penulis (a, w)
dan menghapus kendala yang w
seharusnya 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 P
adalah functor karena a
hanya 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
Applicative
bukanMonad
karena tanda tangan jenis bind
tidak dapat diimplementasikan.
Saya tidak tahu contoh seperti itu!
- Sebuah functor yang
Applicative
tetapi bukanMonad
karena hukum tidak dapat dipenuhi meskipun jenis tanda tangan bind
dapat 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 Monad
contoh yang sah . Alasan untuk perilaku non-monadik adalah bahwa tidak ada cara alami untuk mengimplementasikan bind
ketika suatu fungsi f :: a -> B b
dapat kembali Nothing
atau Just
untuk 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 Nothing
juga 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 bind
yang memenuhi hukum naturalitas dan asosiasi, tetapi pure
metodenya gagal dalam salah satu hukum identitas. "Semimonad adalah semigroup dalam kategori endofunctor, apa masalahnya?" Ini adalah kelas tipe Bind
.
Untuk mempermudah, saya mendefinisikan join
metode 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 Monad
contoh yang sah , meskipun mereka jelas Applicative
.
Fungsi-fungsi ini tidak memiliki trik - tidak ada Void
atau di bottom
mana pun, tidak ada kemalasan / keketatan yang rumit, tidak ada struktur tanpa batas, dan tidak ada batasan kelas tipe. Mesin Applicative
virtual benar-benar standar. Fungsi return
dan bind
dapat 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 a
adalah 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 Monad
jenis polinomial yang sah menurut hukum . Dalam semua konstruksi ini, M
adalah monad:
type M a = Either c (w, a)
di mana w
ada monoid
type M a = m (Either c (w, a))
di mana m
ada monad dan w
monoid
type M a = (m1 a, m2 a)
di mana m1
dan m2
apa pun monad
type M a = Either a (m a)
dimanakah m
monad?
Konstruksi pertama adalah WriterT w (Either c)
, konstruksi kedua adalah WriterT w (EitherT c m)
. Konstruksi ketiga adalah produk bijak komponen: pure @M
didefinisikan sebagai produk bijak komponen pure @m1
dan pure @m2
, dan join @M
didefinisikan 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
, a
dan 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) Monad
instance.
Masih harus dilihat, kasus penggunaan apa yang mungkin ada untuk monad semacam itu. Masalah lain adalah bahwa Monad
turunan yang diturunkan melalui konstruksi 1-4 pada umumnya tidak unik. Sebagai contoh, konstruktor tipe type F a = Either a (a, a)
dapat diberikan Monad
contoh 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 Monad
instance. 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
?