Ответ 1
Я думаю, что это проще всего понять, посмотрев на оператор join:
join :: Monad m => m (m a) -> m a
join является альтернативой >>= для определения Monad, и ее немного легче рассуждать. (Но теперь у вас есть упражнение: покажите, как реализовать >>= из join и как реализовать join из >>=!)
Давайте попробуем сделать операцию join для Composed f g и посмотрим, что пойдет не так. Наш ввод по сути является значением типа f (g (f (g a))), и мы хотим получить значение типа f (g a). Мы также знаем, что у нас есть join для f и g по отдельности, поэтому, если бы мы могли получить значение типа f (f (g (g a))), то мы могли бы нажать его с fmap join . join, чтобы получить f (g a), который мы хотели.
Теперь f (f (g (g a))) не так далеко от f (g (f (g a))). Все, что нам действительно нужно, это такая функция: distribute :: g (f a) -> f (g a). Тогда мы могли бы реализовать join так:
join = Compose . fmap join . join . fmap (distribute . fmap getCompose) . getCompose
Примечание: есть некоторые законы, которые мы хотели бы выполнить distribute, чтобы убедиться, что получаемый нами join законен.
Итак, это показывает, как мы можем составить две монады, если у нас есть закон распределения distribute :: (Monad f, Monad g) => g (f a) -> f (g a). Теперь, это может быть правдой, что каждая пара монад имеет закон распределения. Может быть, нам просто нужно серьезно задуматься о том, как записать один?
К сожалению, есть пары монад, у которых нет закона распределения. Таким образом, мы можем ответить на ваш первоначальный вопрос, создав две монады, которые определенно не могут превратить g (f a) в f (g a). Эти две монады будут свидетельствовать о том, что монады вообще не сочиняются.
Я утверждаю, что у g = IO и f = Maybe нет закона о распределении
-- Impossible!
distribute :: IO (Maybe a) -> Maybe (IO a)
Давайте подумаем, почему такая вещь невозможна. Входом для этой функции является действие ввода-вывода, которое выходит в реальный мир и в конечном итоге производит Nothing или Just x. Выход этой функции - либо Nothing, либо Just действие ввода-вывода, которое при запуске в конечном итоге приводит к x. Чтобы создать Maybe (IO a), нам нужно заглянуть в будущее и предсказать, что будет делать действие IO (Maybe a)!
В итоге:
- Монады могут сочинять, только если существует закон распределения
g (f a) -> f (g a). - Некоторые монады не имеют такого закона распределения.
- Некоторые монады могут сочинять друг с другом, но не каждая пара монад может сочинять.

