I have this code with State monads:
import Control.Monad.State
data ModelData = ModelData String
data ClientData = ClientData String
act :: String -> State ClientData a -> State ModelData a
act _ action = do
let (result, _) = runState action $ ClientData ""
return result
addServer :: String -> State ClientData ()
addServer _ = return ()
scenario1 :: State ModelData ()
scenario1 = do
act "Alice" $ addServer "https://example.com"
I am trying to generalise it with polymorphic type-classes following this approach: https://serokell.io/blog/tagless-final.
I can generalise ModelData:
import Control.Monad.State
class Monad m => Model m where
act :: String -> State c a -> m a
data Client = Client String
addServer :: String -> State Client ()
addServer _ = return ()
scenario1 :: Model m => m ()
scenario1 = do
act "Alice" $ addServer "https://example.com"
But when I try to do it with both ModelData and ClientData it fails to compile:
module ExampleFailing where
class Monad m => Model m where
act :: Client c => String -> c a -> m a
class Monad c => Client c where
addServer :: String -> c ()
scenario1 :: Model m => m ()
scenario1 = do
act "Alice" $ addServer "https://example.com"
The error:
• Could not deduce (Client c0) arising from a use of ‘act’
from the context: Model m
bound by the type signature for:
scenario1 :: forall (m :: * -> *). Model m => m ()
at src/ExampleFailing.hs:9:1-28
The type variable ‘c0’ is ambiguous
• In the expression: act "Alice"
In a stmt of a 'do' block:
act "Alice" $ addServer "https://example.com"
In the expression:
do act "Alice" $ addServer "https://example.com"
|
11 | act "Alice" $ addServer "https://example.com"
| ^^^^^^^^^^^
I can make it compile this way, but it seems different from the original code I am trying to generalise:
{-# LANGUAGE MultiParamTypeClasses #-}
module ExamplePassing where
class Monad m => Model m c where
act :: Client c => String -> c a -> m (c a)
class Monad c => Client c where
addServer :: String -> c ()
scenario1 :: (Client c, Model m c) => m (c ())
scenario1 = do
act "Alice" $ addServer "https://example.com"
I would really appreciate your advice. Thank you!