Подтвердить что ты не робот

Существуют ли контравариантные монады?

Функторы могут быть ковариантными и контравариантными. Может ли эта ковариантная/контравариантная двойственность применяться к монадам?

Что-то вроде:

class Monad m where
  return :: a -> m a
  (>>=) :: m a -> (a -> m b) -> m b    

class ContraMonad m where
  return :: a -> m a
  contrabind :: m a -> (b -> m a) -> m b

Имеет ли смысл класс ContraMonad? Любые примеры?

4b9b3361

Ответ 1

Ну, конечно, это можно определить, но я сомневаюсь, что это будет полезно.

Существует популярное высказывание, что "монада - это всего лишь моноид в категории эндофенторов". Это означает, прежде всего, что у нас есть категория endofunctors (что означает (ковариантные) функторы от какой-то категории к себе), и что еще, мы имеем некоторое умножение на этом endofunctors (в данном случае - композиция). И тогда монада вписывается в некоторые общие рамки, о которых нам сейчас не нужно беспокоиться. Дело в том, что "умножение" контравариантных функторов отсутствует. Состав двух ковариантных функторов снова является ковариантным функтором; но состав двух контравариантных функторов не является контравариантным функтором (скорее это ковариантный функтор, поэтому совершенно другой зверь).

Итак, "контравариантные монады" действительно не имеют смысла.

Ответ 2

Контравариантный функтор является функтором из одной категории в его противоположную категорию, т.е. из одной категории в другую (хотя и тесно связанную). OTOH, монада - это, прежде всего, эндофуктор, т.е. Из одной категории в себя. Поэтому он не может быть контравариантным.

Этот вид материала всегда имеет тенденцию быть намного яснее, когда вы рассматриваете "фундаментальное математическое" определение монадов:

class Functor m => Monad m where
  pure :: a -> m a
  join :: m (m a) -> m a

Как вы видите, на самом деле нет никаких стрелок, которые могли бы развернуться в результате, как вы делали с contrabind. Конечно, есть

class Functor n => Comonad n where
  extract :: n a -> a
  duplicate :: n a -> n (n a)

но комонады все еще являются ковариантными функторами.

В отличие от монадов, аппликативы (моноидальные функторы) не обязательно должны быть эндофенторами, поэтому я считаю, что их можно оборачивать. Давайте начнем с "фундаментального" определения:

class Functor f => Monoidal f where
  pureUnit :: () -> f ()
  fzipWith :: ((a,b)->c) -> (f a, f b)->f c  -- I avoid currying to make it clear what the arrows are.

(упражнение: определить производный экземпляр Applicative в терминах этого и наоборот)

Поворот вокруг

class Contravariant f => ContraApp f where
  pureDisunit :: f () -> ()
  fcontraunzip :: ((a,b)->c) -> f c->(f a, f b)
                            -- I'm not sure, maybe this should
                            -- be `f c -> Either (f a) (f b)` instead.

Не знаю, насколько это было бы полезно. pureDisunit, конечно, не полезно, потому что его единственная реализация всегда const ().

Попробуем написать очевидный экземпляр:

newtype Opp a b = Opp { getOpp :: b -> a }

instance Contravariant (Opp a) where
  contramap f (Opp g) = Opp $ g . f

instance ContraApp (Opp a) where
  pureDisunit = const ()
  fcontraunzip z (Opp g)
     = (Opp $ \a -> ???, Opp $ \b -> ???) -- `z` needs both `a` and `b`, can't get it!

Я не думаю, что это полезно, хотя вы можете определить его с чем-то вроде умной рекурсии, связанной с узлом.

Что может быть интереснее контравариантный сомоноидный функтор, но сейчас это становится слишком странным для меня.