Как добиться эффекта "продукта двух монадов"?

Предположим, что у нас две монады, m и m'. Предположим теперь, что у нас есть переменные,

-- in real problems, the restriction is some subclass MyMonad, so don't worry
-- if it the case here that mx and f must essentially be pure.
mx :: Monad m'' => m'' a
f :: Monad m'' => a -> m'' b

Есть ли способ создать что-либо похожее на продукт m x m'? Я знаю, что это возможно со стрелками, но для монад кажется более сложным (невозможно?), Особенно при попытке написать, что должен делать mx >>= f.

Чтобы увидеть это, определите

data ProdM a = ProdM (m a) (m' a)
instance Monad ProdM where
    return x = ProdM (return x) (return x)

но теперь, когда мы определяем mx >>= f, он не понимает, какое значение из mx перейти к f,

    (ProdM mx mx') >>= f
        {- result 1 -} = mx >>= f
        {- result 2 -} = mx' >>= f

Я хочу, чтобы (mx >>= f) :: ProdM был изоморфен ((mx >>= f) :: m) x ((mx >>= f) :: m').

Ответ 1

Да, этот тип - монада. Ключ просто передать оба результата в f и сохранить поле соответствия только из результата. То есть, мы сохраняем первый элемент из результата передачи результата mx, а второй результат результата результата mx'. Экземпляр выглядит следующим образом:

instance (Monad m, Monad m') => Monad (ProdM m m') where
  return a = ProdM (return a) (return a)
  ProdM mx mx' >>= f = ProdM (mx >>= fstProd . f) (mx' >>= sndProd . f)
    where fstProd (ProdM my _) = my
          sndProd (ProdM _ my') = my'

ProdM доступен в пакете monad-products под названием Product.