Я хотів би запропонувати більш систематичний підхід до відповіді на це запитання, а також показати приклади, які не використовують жодних спеціальних хитрощів, таких як "найнижчі" значення чи нескінченні типи даних чи щось подібне.
Коли у конструкторів типів відсутні екземпляри класу типу?
Загалом, є дві причини, чому конструктор типів не може мати примірник певного класу типу:
- Неможливо реалізувати підписи типів необхідних методів із класу типу.
- Може реалізувати підписи типів, але не може задовольнити необхідні закони.
Приклади першого типу простіші за типи другого роду, тому що для першого типу нам просто потрібно перевірити, чи можна реалізувати функцію з підписом заданого типу, тоді як для другого роду нам потрібно довести, що жодної реалізації могло б задовольнити закони.
Конкретні приклади
Це контрафунктор, а не функтор щодо параметра типу a
, тому що a
в противаріантному положенні. Неможливо реалізувати функцію з підписом типу (a -> b) -> F z a -> F z b
.
Конструктор типів, який не є законним функціонером, навіть якщо підпис типу fmap
може бути реалізований:
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!
Цікавим аспектом цього прикладу є те, що ми можемо реалізувати fmap
правильний тип, навіть F
не може бути функтором, оскільки він використовує a
в протилежній позиції. Таким чином, ця реалізація, fmap
показана вище, вводить в оману - навіть якщо вона має правильний підпис типу (я вважаю, що це єдина можлива реалізація підпису цього типу), закони функтора не задовольняються. Наприклад, fmap id
≠ id
, тому що let (Q(f,_)) = fmap id (Q(read,"123")) in f "456"
є 123
, але let (Q(f,_)) = id (Q(read,"123")) in f "456"
є456
.
Насправді F
це лише профунктор, - він не є ні функтором, ні контрафунктором.
Законний функтор, який не застосовується, оскільки підпис типу pure
не може бути реалізований: візьміть монаду Writer (a, w)
і усуньте обмеження, яке w
повинно бути моноїдом. Тоді неможливо побудувати значення типу (a, w)
з a
.
Функтор , яка не апплікатівен , так як тип підпис <*>
не може бути реалізована: data F a = Either (Int -> a) (String -> a)
.
Функтор, який не є законним застосунком, незважаючи на те, що методи класу типу можуть бути реалізовані:
data P a = P ((a -> Int) -> Maybe a)
Конструктор типу P
- це функтор, оскільки він використовується a
лише в коваріантних положеннях.
instance Functor P where
fmap :: (a -> b) -> P a -> P b
fmap fab (P pa) = P (\q -> fmap fab $ pa (q . fab))
Єдина можлива реалізація підпису типу <*>
- це функція, яка завжди повертає Nothing
:
(<*>) :: P (a -> b) -> P a -> P b
(P pfab) <*> (P pa) = \_ -> Nothing -- fails the laws!
Але ця реалізація не відповідає закону про ідентичність для прикладних функціонерів.
- Функтор, який є,
Applicative
але не є,Monad
тому що підпис типу bind
не може бути реалізований.
Я не знаю таких прикладів!
- Функтор, який є,
Applicative
але не є,Monad
тому що закони не можуть бути виконані, навіть якщо підпис типу bind
може бути реалізований.
Цей приклад породив досить багато дискусій, тому можна з упевненістю сказати, що довести цей приклад правильним непросто. Але кілька людей це перевірили самостійно різними методами. Див. `Дані PoE a = Порожня | Пара aa` монада? для додаткового обговорення.
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
Дещо громіздко довести, що не існує законної Monad
інстанції. Причина немональної поведінки полягає в тому, що не існує природного способу реалізації, bind
коли функція f :: a -> B b
може повернутися Nothing
або Just
для різних значеньa
.
Можливо, зрозуміліше врахувати Maybe (a, a, a)
, що також не є монадою, і спробувати здійснити join
для цього. Виявите, що немає інтуїтивно розумного способу реалізації 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.
У випадках, зазначених у ???
, видається очевидним, що ми не можемо виготовити Just (z1, z2, z3)
будь-який розумний і симетричний з шести різних типів значень a
. Ми, безумовно, могли вибрати якийсь довільний підмножина з цих шести значень, - наприклад, завжди приймати перше не порожнє Maybe
- але це не задовольнило б закони монади. Повернення Nothing
також не буде задовольняти закони.
- Деревоподібна структура даних, яка не є монадою, хоча і має асоціативність,
bind
але не відповідає законам ідентичності.
Звичайна деревна монада (або «дерево з гілками у формі функтора») визначається як
data Tr f a = Leaf a | Branch (f (Tr f a))
Це вільна монада над функтором f
. Форма даних - це дерево, де кожна точка гілки є "функтор-фул" підрядів. Стандартне двійкове дерево було б отримане за допомогою type f a = (a, a)
.
Якщо ми модифікуємо цю структуру даних, створюючи також листя у формі функтора f
, ми отримуємо те, що я називаю «семімонадою» - це таке, bind
яке задовольняє закони природності та асоціативності, але його pure
метод не відповідає одному із законів тотожності. "Семімонади - це напівгрупи в категорії ендофаніків. У чому проблема?" Це клас типу Bind
.
Для простоти я визначаю join
метод замість 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)
Філія щеплення є стандартною, але лист щеплення не є стандартною і виробляє Branch
. Це не є проблемою для закону про асоціативність, але порушує один із законів тотожності.
Коли поліноміальні типи мають монадні екземпляри?
Жоден з функціонерів Maybe (a, a)
і не Maybe (a, a, a)
може бути наділений законним Monad
екземпляром, хоча вони, очевидно Applicative
.
У цих функторів немає жодних хитрощів - ні Void
і bottom
ніде, немає хитрої ліні / суворості, нескінченних структур і жодних обмежень класу типів. Applicative
Примірник повністю стандартні. Функції return
і bind
можуть бути реалізовані для цих функторів, але не відповідають законам монади. Іншими словами, ці функтори не є монадами, оскільки конкретна структура відсутня (але зрозуміти, чого саме не вистачає, непросто). Наприклад, невелика зміна функтора може перетворити його в монаду: data Maybe a = Nothing | Just a
це монада. Ще один подібний функторdata P12 a = Either a (a, a)
- це монада.
Конструкції для поліноміальних монад
Загалом, ось деякі конструкції, які випускають законні Monad
з поліноміальних типів. У всіх цих конструкціях M
є монада:
type M a = Either c (w, a)
де w
будь-який моноїд
type M a = m (Either c (w, a))
де m
будь-яка монада і w
будь-який моноїд
type M a = (m1 a, m2 a)
де m1
і m2
є якісь монади
type M a = Either a (m a)
де m
будь-яка монада
Перша конструкція WriterT w (Either c)
, друга конструкція WriterT w (EitherT c m)
. Третя конструкція є складовою продуктом монад: pure @M
визначається як компонентний добуток pure @m1
і pure @m2
, і join @M
визначається, опускаючи дані про поперечні продукти (наприклад m1 (m1 a, m2 a)
, відображається m1 (m1 a)
шляхом опускання другої частини кортежу):
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))
Четверта конструкція визначається як
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
Я перевірив, що всі чотири конструкції виробляють законні монади.
Я здогадуюсь, що немає інших конструкцій для поліноміальних монад. Наприклад, функтор Maybe (Either (a, a) (a, a, a, a))
не отримується за допомогою будь-якої з цих конструкцій і тому не є монадичним. Тим НЕ менше, Either (a, a) (a, a, a)
це Монадический тому вона ізоморфна добутку трьох монад a
, a
і Maybe a
. Крім того, Either (a,a) (a,a,a,a)
є монадичним, оскільки є ізоморфним продукту a
і Either a (a, a, a)
.
Чотири конструкції, показані вище, дозволять нам отримати будь-яку суму будь-якої кількості продуктів будь-якої кількості a
, наприклад, Either (Either (a, a) (a, a, a, a)) (a, a, a, a, a))
тощо. Усі конструктори такого типу матимуть (принаймні один) Monad
екземпляр.
Зрозуміло, залишається зрозуміти, які випадки використання можуть існувати для таких монад. Інша проблема полягає в тому, що Monad
випадки, отримані за допомогою конструкцій 1-4, як правило, не є унікальними. Наприклад, конструктору типу type F a = Either a (a, a)
можна надати Monad
екземпляр двома способами: шляхом побудови 4 за допомогою монади (a, a)
та за допомогою конструкції 3 за допомогою ізоморфізму типу Either a (a, a) = (a, Maybe a)
. Знову ж таки, пошук випадків використання для цих реалізацій не відразу очевидний.
Залишається питання - даючи довільний тип поліноміальних даних, як розпізнати, чи має він Monad
екземпляр. Я не знаю, як довести, що немає інших конструкцій для поліноміальних монад. Я не думаю, що поки що існує якась теорія, яка б відповіла на це питання.
* -> *
), для якого немає відповідногоfmap
?