Я хотел бы предложить более систематический подход к ответу на этот вопрос, а также показать примеры, в которых не используются какие-либо специальные приемы, такие как «нижние» значения или бесконечные типы данных или что-либо подобное.
Когда конструкторы типов не могут иметь экземпляры классов типов?
В общем, есть две причины, по которым конструктор типов может не иметь экземпляра определенного класса типов:
- Невозможно реализовать сигнатуры типов необходимых методов из класса типов.
- Может реализовывать подписи типа, но не может соответствовать требуемым законам.
Примеры первого типа проще, чем второго типа, потому что для первого нам нужно просто проверить, можно ли реализовать функцию с заданной сигнатурой типа, в то время как для второго типа мы должны доказать, что реализация отсутствует мог бы удовлетворить законы.
Конкретные примеры
Это контрафунктор, а не функтор, относительно параметра типа 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 = Пусто | Пара монад? для дополнительного обсуждения.
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
s из полиномиальных типов. Во всех этих конструкциях 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
?