Поперше:
Будь-яка монада також є прикладним функціонером, а будь-який прикладний функтор - функтор.
Це вірно в контексті Хаскелла, але (читання Applicative
як "сильний млявий моноїдальний функтор") взагалі не з досить тривіальної причини, що ви можете мати "прикладних" функторів між різними моноїдними категоріями, тоді як монади (і комонади) є ендофункторами .
Крім того, ототожнення Applicative
з сильними розслабленими моноїдними функторами злегка вводить в оману, тому що для обгрунтування назви (та підпису типу (<*>)
) потрібен функтор між закритими моноїдними категоріями, що зберігає як моноїдну структуру, так і внутрішній хом . Це, правдоподібно, можна назвати "млявим замкнутим моноїдальним функтором", за винятком того, що функтор між моноїдальними закритими категоріями, що зберігає будь-яку властивість, зберігає інший очевидним чином . Оскільки Applicative
описуються лише ендофайнери на Hask, що зберігають моноїдну структуру (,)
, його екземпляри автоматично набувають безліч властивостей, включаючи їх силу , яку, таким чином, можна уникнути.
Очевидний зв'язок з Monad
, мабуть, є артефактом імпліцитних обмежень у тому, що Applicative
змушують збігатися аспекти їхніх моноїдних структур, щасливий збіг, який, на жаль, не переживає дуалізації.
Так само, як комонада на категорії - це монада на C o p , опіксальний моноїдний функтор C → D - млявий моноїдальний функтор C o p → D o p . Але H s до про р НЕ моноідальная закритий , і з- що не включає в себе функцію додатка навряд чи заслуговує назви. У будь-якому випадку, результат не був би надзвичайно цікавим:CCop C→DCop→DopHaskopApplicative
class (Functor f) => CoMonoidal f where
counit :: f () -> ()
cozip :: f (a, b) -> (f a, f b)
Натомість ми могли б уявити собі поняття "колакс-закритий функтор", який би виглядав набагато більше, Applicative
якби він існував. На жаль, взагалі не є (наскільки мені відомо) закритою категорією: в H a s k відповідає морфізмам b → a в H a s k o p , але не працює як внутрішній hom там - тому що стрілки повернуті назад, потрібна буде якась функція, яку ми не можемо визначити загалом для H a s k .Haskopnewtype Op b a = Op (a -> b)
Haskb→aHaskopOp b a
Hask
Якщо ми просто зробимо вигляд, що "колективні закриті функтори" існували для , і, крім того, працювали так, як ми би наївно сподіваємось, що це буде, співавтор на цьому, мабуть, виглядатиме так:HaskApplicative
class (Functor f) => CoApplicative f where
copure :: f a -> a
coap :: (f a -> f b) -> f (a -> b)
Додавання duplicate :: f a -> f (f a)
до copure
вироблятиме комонаду (припускаючи, що закони задоволені), звичайно. Але очевидного зв’язку між coap
- і яким би воно не було - немає extend :: (f a -> b) -> f a -> f b
. Порівнюючи типи, стає зрозуміло, що дуалізація відбувається по-різному: комоїдальні структури, що лежать в основі, duplicate
і cozip
мають мало спільного між собою або з coap
(що, мабуть, не має сенсу), тоді як liftA2 (,)
і (<*>)
є рівнозначними і можуть бути отримані з них join
.
Ще одним можливим способом дуалізації Applicative
, який має ще менше спільного з комонадами, є розгляд противаріантних моноїдних функторів:
class (Contravariant f) => ContraMonoidal f where
contraunit :: f a
contrazip :: f a -> f b -> f (Either a b)
Але це стикається з тими ж питаннями, що і вище, а саме, що не є закритою категорією. Якби це було, ми мали б певний тип таким чином, що ми могли б написати такі функції , як і і так далі , що на насправді працювали , як і очікувалося.Haskopb <~ a
contracurry :: (Either c b <~ a) -> (c <~ (b <~ a))
contraapply :: b -> Either a (a <~ b)
Якщо пам'ять слугує мені, перешкоди тут не характерні лише для Haskell, а швидше виникають з HaskCoApplicative
Однак у моноїдальній закритій категорії, більш гостинній до дуалізації, ви можете мати більше удачі. Зокрема, я вважаю, що Kleisli (Cont r)
і її, і протилежна категорія є моноїдними, тому це може бути кращим контекстом для вивчення цих ідей.