在 haskell 中使用新类型隐藏嵌套状态转换器
Hiding nested state transformers with newtype in haskell
我不确定我想要完成的事情是否理智(请保持友善)。但是我有一个小游戏的想法,游戏需要有一些状态并且状态用一些随机组件更新(否则它会有点无聊)。看到 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'
我是否需要为我的 Game
和 Rng
类型实现 MonadTrans
?
任何帮助都会很棒!
我想我明白了。如果我像这样更改 mainGame
:
mainGame :: Game ()
mainGame = do
liftIO $ print "Inside game"
s <- gets
n <- Game . lift $ randoms 10 :: (Rng [Int])
put s ++ n
它按预期工作。看看 MonadTrans lift 的定义,这似乎就是我所缺少的。所以制作一个 MonadTrans 的 Game 实例可以解决我的问题。
因此对 Game
类型进行以下更改:
newtype GameT m a {
runG :: StateT GameS m a
} deriving (Applicative, Functor, Monad, MonadIO, MonadState, MonadTrans GameS)
type Game = GameT Rng
会让我做我想做的事。我仍然不太清楚为什么我必须用签名 GameT m a
制作 GameT
才能制作 MonadTrans
的实例,在这里与类型斗争,也许有人可以 chimn
我不确定我想要完成的事情是否理智(请保持友善)。但是我有一个小游戏的想法,游戏需要有一些状态并且状态用一些随机组件更新(否则它会有点无聊)。看到 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'
我是否需要为我的 Game
和 Rng
类型实现 MonadTrans
?
任何帮助都会很棒!
我想我明白了。如果我像这样更改 mainGame
:
mainGame :: Game ()
mainGame = do
liftIO $ print "Inside game"
s <- gets
n <- Game . lift $ randoms 10 :: (Rng [Int])
put s ++ n
它按预期工作。看看 MonadTrans lift 的定义,这似乎就是我所缺少的。所以制作一个 MonadTrans 的 Game 实例可以解决我的问题。
因此对 Game
类型进行以下更改:
newtype GameT m a {
runG :: StateT GameS m a
} deriving (Applicative, Functor, Monad, MonadIO, MonadState, MonadTrans GameS)
type Game = GameT Rng
会让我做我想做的事。我仍然不太清楚为什么我必须用签名 GameT m a
制作 GameT
才能制作 MonadTrans
的实例,在这里与类型斗争,也许有人可以 chimn