I'm trying to translate this Scala's cats
example about composing free monads.
The gist of the example seems to be the decomposition of separate concerns into separate data types:
data Interact a = Ask (String -> a) | Tell String a deriving (Functor)
data DataOp = AddCat String | GetAllCats [String] deriving (Functor)
type CatsApp = Sum Interact DataOp
Without having these two separate concerns, I would build the "language" for Interact
operations as follows:
ask :: Free Interact String
ask = liftF $ Ask id
tell :: String -> Free Interact ()
tell str = liftF $ Tell str ()
However, if I want to use ask
and tell
in a program that also uses DataOp
I cannot define them with the types above, since such a program will have type:
program :: Free CatsApp a
In cats
, for the definition of the tell
and ask
operations they use an InjectK
class, and an inject
method from Free
:
class Interacts[F[_]](implicit I: InjectK[Interact, F]) {
def tell(msg: String): Free[F, Unit] = Free.inject[Interact, F](Tell(msg))
def ask(prompt: String): Free[F, String] = Free.inject[Interact, F](Ask(prompt))
}
What puzzles me is that somehow the positional information of the functors (DataOp
is on the left, Interact
is on the right) seems to be irrelevant (which is quite nice).
Are there similar type-classes and functions that could be used to solve this problem in an elegant manner using Haskell?
implicit I: InjectK[Interact, F]
, which tells you how to injectInteract
s intoF
s. WhereverInteracts
is instantiated will have to give that information. – luqui