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

Биапликативный и бимонад?

Haskell Data.Bifunctor в основном:

class Bifunctor f where
  bimap :: (a -> c) -> (b -> d) -> f a b -> f c d 

Я мог бы найти Biapply. Мой вопрос в том, почему нет полной би-иерархии (иерархии?), Например:

class Bifunctor f => Biapplicative f where
  bipure :: a -> b -> f a b
  biap :: f (a -> b) (c -> d) -> f a c -> f b d 

class Biapplicative m => Bimonad m where
  bibind :: m a b -> (a -> b -> m c d) -> m c d

  bireturn :: a -> b -> m a b
  bireturn = bipure

bilift :: Biapplicative f => (a -> b) -> (c -> d) -> f a c -> f b d
bilift f g = biap $ bipure f g 

bilift2 :: Biapplicative f => (a -> b -> c) -> (x -> y -> z) -> f a x -> f b y -> f c z
bilift2 f g = biap . biap (bipure f g)

Пара - это пример:

instance Bifunctor (,) where
  bimap f g (x,y) = (f x, g y)

instance Biapplicative (,) where
  bipure x y = (x,y)
  biap (f,g) (x,y) = (f x, g y)

instance Bimonad (,) where
  bibind (x,y) f = f x y

И типа типа...

data Maybe2 a b = Fst a | Snd b | None
--or 
data Or a b = Both a b | This a | That b | Nope

... имел бы ИМО также экземпляры.

Не хватает ли подходящих типов? Или что-то в том, что мой код сильно испорчен?

4b9b3361

Ответ 1

Монада в теории категорий является эндофункцией, т.е. функтором, где область и кодомен имеют одну и ту же категорию. Но a Bifunctor является функтором из категории продуктов Hask x Hask до Hask. Но мы могли бы попытаться выяснить, как выглядит монада в категории Hask x Hask. Это категория, где объекты представляют собой пары типов, т.е. (a, b), а стрелки - это пары функций, то есть стрелка от (a, b) до (c, d) имеет тип (a -> c, b -> d). Энтундуктор в этой категории отображает пары типов в пары типов, т.е. От (a, b) до (l a b, r a b), а пары стрелок - к парам стрелок, т.е.

(a -> c, b -> d) -> (l a b -> l c d, r a b -> r c d)

Если вы разделите эту функцию карты на 2, вы увидите, что endofunctor в Hask x Hask совпадает с двумя Bifunctor s, l и r.

Теперь для монады: return и join являются стрелками, поэтому в этом случае обе являются 2-мя функциями. return - стрелка от (a, b) до (l a b, r a b), а join - стрелка от (l (l a b) (r a b), r (l a b) (r a b)) до (l a b, r a b). Это выглядит так:

class (Bifunctor l, Bifunctor r) => Bimonad l r where
  bireturn :: (a -> l a b, b -> r a b)
  bijoin :: (l (l a b) (r a b) -> l a b, r (l a b) (r a b) -> r a b)

Или выделено:

class (Bifunctor l, Bifunctor r) => Bimonad l r where
  bireturnl :: a -> l a b
  bireturnr :: b -> r a b
  bijoinl :: l (l a b) (r a b) -> l a b
  bijoinr :: r (l a b) (r a b) -> r a b

И подобно m >>= f = join (fmap f m), мы можем определить:

  bibindl :: l a b -> (a -> l c d) -> (b -> r c d) -> l c d
  bibindl lab l r = bijoinl (bimap l r lab)
  bibindr :: r a b -> (a -> l c d) -> (b -> r c d) -> r c d
  bibindr rab l r = bijoinr (bimap l r rab)

Относительные монады

В последнее время были разработаны относительные монады. Относительная монада не обязательно должна быть эндофуктором! Если мы переведем из статьи в Bifunctor в Haskell, вы получите:

class RelativeBimonad j m where
  bireturn :: j a b -> m a b
  bibind :: m a b -> (j a b -> m c d) -> m c d

Определяет монаду относительно бифунтера j. Если вы выберите j как (,), вы получите свое определение.

Законы те же, что и законы монады:

bireturn jab `bibind` k = k jab
m `bibind` bireturn = m
m `bibind` (\jab -> k jab `bibind` h) = (m `bibind` k) `bibind` h

Первый закон запрещает Maybe2 быть экземпляром, потому что bibind должен иметь возможность извлекать оба значения из результата bireturn.