Может ли существовать mtl-подобный механизм для преобразователей монад, созданных FreeT / ProgramT?
Я понимаю историю следующим образом. Давным-давно был изобретен преобразователь монад. Потом люди начали складывать монадные преобразователи один на другой, потом стало раздражать вставлять lift
везде. Затем несколько человек изобрели классы монад, чтобы мы могли, например, ask :: m r
в любой монаде m
такой, что MonadReader r m
. Это стало возможным благодаря тому, что каждый класс монад проникал в каждый преобразователь монад, например
(Monoid w, MonadState s m) => MonadState s (WriterT w m)
MonadWriter w m => MonadWriter w (StateT s m)
вам нужна такая пара объявлений экземпляров для каждой пары преобразователей монад, поэтому при наличии n преобразователей монад требуется n ^ 2 затрат. Однако это не было большой проблемой, потому что люди в основном будут использовать предопределенные монады и редко создают свои собственные. История, которую я понимаю, также подробно описана, например, в следующих вопросах и ответах:
Как избежать подъема с помощью преобразователей монад
Тогда моя проблема связана с новыми бесплатными монадами http://hackage.haskell.org/package/free и Операционные монады http://hackage.haskell.org/package/operational. Они позволяют нам писать собственный DSL и использовать его как монады, просто определяя язык как некоторый алгебраический data
тип (Operational даже не требует Functor
экземпляров). Хорошая новость в том, что у нас могут быть монады и преобразователи монад бесплатно; тогда как насчет классов монад? Плохая новость заключается в том, что предположение «мы редко определяем собственные преобразователи монад» больше не актуально.
Пытаясь разобраться в этой проблеме, я сделал два ProgramT
и заставил их проникать друг в друга;
https://github.com/nushio3/practice/blob/master/operational/exe-src/test-05.hs
Пакет operational
не поддерживает классы монад, поэтому я взял другую реализацию minioperational
и изменил ее, чтобы она работала так, как мне нужно; https://github.com/nushio3/minioperational
Тем не менее, мне нужно было объявление специализированного экземпляра
instance (Monad m, Operational ILang m) => Operational ILang (ProgramT SLang m) where
потому что общее объявление следующей формы приводит к неразрешимым примерам.
instance (Monad m, Operational f m) => Operational f (ProgramT g m) where
Мой вопрос в том, как мы можем упростить проникновение наших Операционных монад друг в друга. Или мое желание проникнуть в любую Оперативную монаду некорректно.
Я также хотел бы знать правильный технический термин для проникновения :)