I have this polymorphic code (see this question) with generic monads for model and client:
import Control.Monad.Writer
class Monad m => Model m where
act :: Client c => String -> c a -> m a
class Monad c => Client c where
addServer :: String -> c ()
scenario1 :: forall c m. (Client c, Model m) => m ()
scenario1 = do
act "Alice" $ addServer @c "https://example.com"
and this is the pretty-print interpreter for the Client
that explains the actions in the log via Writer monad:
type Printer = Writer [String]
instance Client Printer where
addServer :: String -> Printer ()
addServer srv = tell [" add server " ++ srv ++ "to the client"]
Interpreter for the Model
is difficult. I tried several things, each resulting in its own error:
- "Couldn't match type ‘c’":
instance Model Printer where
act :: String -> Printer a -> Printer a
act name action = do
tell [name ++ ":"]
action
- "`Cannot apply expression of type ‘Printer a’ to a visible type argument ‘(Printer a)’":
instance Model Printer where
act :: forall a. String -> Printer a -> Printer a
act name action = do
tell [name ++ ":"]
action @(Printer a)
- "Couldn't match type ‘c’ with ‘WriterT [String] Data.Functor.Identity.Identity’"
instance Model Printer where
act :: Client c => String -> c a -> Printer a
act name action = do
tell [name ++ ":"]
action
Somehow I need to tell that what was c a
in act
is now Printer a
.
Maybe I need to have two parameters in the Model class - m
for Model monad and c
for Client monad, and Model class should also define the function clientToModel :: c a -> m a
?
Is there a way to keep Model and Client decoupled? I probably would still need clientToModel :: c a -> m a
for each pair?
I appreciate the advice. Thank you!