Я не уверен, чего я хочу добиться, разумно или нет (пожалуйста, будьте любезны). Но у меня была идея для небольшой игры, в игре должно быть какое-то состояние, и состояние обновляется каким-то случайным компонентом (иначе было бы скучно). Поскольку StdGen также является своего рода состоянием, я начал моделировать свою программу следующим образом.
import Control.Monad.State
import qualified System.Random as R
type Rng = StateT StdGen IO
random :: (Random a) => Rng a
random = state R.random
randoms :: (Random a) => Int -> Rng [a]
randoms n = replicateM n random
type GameS = [Int]-- not important right now
type Game = StateT GameS Rng
mainGame :: Game ()
mainGame = do
s <- gets
n <- lift $ randoms 10 :: (Rng [Int])
put s ++ n
mainRng :: Rng ()
mainRng = do
liftIO $ "Inside Rng!"
-- here do stuff that has to do with game setup and so on dependent on rng
runStateT mainGame [1,2,3,4]
main :: IO ()
main = do
g <- R.newStdGen
runStateT mainRng g
Хорошо, это сработало! Итак, давайте попробуем скрыть некоторые наши детали за новым типом.
-- change type aliases for Game and Rng to newtype
newtype Rng a {
runR :: StateT R.StdGen IO a
} deriving (Applicative, Functor, Monad, MonadIO, MonadState R.StdGen)
newtype Game a {
runG :: StateT GameS Rng a
} deriving (Applicative, Functor, Monad, MonadIO, MonadState GameS)
-- and create a expose newRun functions
runRng :: Rng a -> IO (a, R.StdGen)
runRng k = do
g <- R.newStdGen
runStateT (runR k) g
runGame :: Game a -> Rng (a, GameS)
runGame k = let initial = [1,2,3]
in runStateT (runG k) initial
-- mainGame as before
mainGame :: Game ()
mainGame = do
liftIO $ print "Inside game"
s <- gets
n <- lift $ randoms 10 :: (Rng [Int])
put s ++ n
main :: IO ()
main = do
final <- runRng $ do
liftIO $ print "Inside rng moand"
runGame mainGame
print $ show final
Это работает до определенного момента. Внутри mainGame
я могу выполнять liftIO и все операции с состоянием, за исключением случаев, когда я пытаюсь lift
получить несколько случайных чисел, я получаю сообщение об ошибке couldn't match type 't0 Rng' with 'Game'
Нужно ли мне как-то реализовать MonadTrans
для моих типов Game
и Rng
?
Любая помощь с этим будет здорово!