У меня есть две бесплатные монады для разных операций в разных контекстах. Однако один (major
) DSL должен содержать другой (action
), если конкретная операция находится в контексте:
import Control.Monad.Free
data ActionFunctor next = Wait Timeout next
| Read URI next
instance Functor ActionFunctor where
fmap f (Wait timeout next) = Wait timeout (f next)
fmap f (Read uri next) = Read uri (f next)
type Action = Free ActionFunctor
data MajorFunctor next = Log LogText next
| Act Action next
| Send Message
instance Functor MajorFunctor where
fmap f (Log text next) = Log text (f next)
fmap f (Act action next) = Act action (f next)
fmap f (Send message) = Send message
type Major = Free MajorFunctor
Проблема в том, что GHC будет жаловаться MajorFunctor
на то, что Action
в Act Action next
является своего рода (* -> *)
, а не просто типом. Это связано с тем, что в определении data ActionFunctor
он должен принимать next
в качестве параметра типа, а в строке Act Action
такого параметра нет. Но даже сообщение для меня ясно, я не уверен, должен ли я объявлять такой дополнительный параметр типа в функторе Major
:
data MajorFunctor actionNext next = ...
Это выглядит странно, потому что только один конструктор данных будет использовать параметр, в то время как такое раскрытие превратит каждое MajorFunctor
в MajorFunctor actionNext
, и это выглядит полностью раскрывающим слишком много деталей. Поэтому я взглянул на трансформатор FreeT
, чтобы убедиться, что это то, что мне нужно. Однако в моем случае мне нужно вызывать интерпретатор Action
только тогда, когда программа DSL имеет такую операцию, а не каждый bind
в монадической программе, поэтому я также не уверен, что преобразователь является хорошим решением.