Haskell ReaderT Design Pattern vs mtl StateT pattern - haskell

I'm designing a small game which basically uses StateT and just updating the state. Below is the simplified version:
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.State
import Control.Monad.State.Class
import System.Random
data PlayerState = PlayerState {
_psName :: String,
_psScore :: Int
} deriving (Show)
makeClassy ''PlayerState
data Game = Game {
_turns :: Int,
_players :: [PlayerState]
} deriving (Show)
makeClassy ''Game
randomGameInit :: IO Game
randomGameInit = do
players <- replicateM 5 $ PlayerState <$> (replicateM 4 $ randomRIO ('a', 'z')) <*> randomRIO (1,10)
return $ Game 0 players
update :: (MonadState s m, HasGame s) => m ()
update = do
players . ix 0 . psName %= (\_ -> "mordor")
turns %= (+1)
exitCondition <- fmap (>10) (turns <%= id)
unless exitCondition update
main :: IO ()
main = do
init <- randomGameInit
runStateT update init >> print "Game Over"
I've recently learned about the ReaderT Design Pattern vs mtl StateT, which encourages replacing StateT with a mutable reference inside a ReaderT over IO.
I wonder how I should adapt the code using ReaderT. Most specifically, many Lens functions have types: (MonadState s m) which apparently need to be inside a State. Does this mean that Lens library functions are designed for StateT and not ReaderT? How to use Lens with ReaderT design pattern?

From what I've seen, ReaderT pattern users typically don't use the MonadState lens operators. Instead, use view to access the desired MVar (or whatever kind of mutable var you're dealing with) and update that as usual (e.g. with modifyMVar).
The RIO monad offers an appropriate MonadState instance, though. A better answer than mine could probably adapt your code to the RIO monad fairly easily.

Related

Avoid wrapping monads in a typed-tagless-final-style DSL

Here's a small toy DSL in typed tagless final style (see Typed Tagless Final Interpreters by O. Kiselyov).
class Monad m => RPCToy m where
mkdir :: FilePath -> m ()
ls :: FilePath -> m [FilePath]
The different instatiations of this little DSL would be, for example, the implementation of mkdir and ls on different platforms, either local and remote. Type m is a monad in all implementations, it could be IO, or one provided by some networking library, or some other homebrew monad.
Here's an implementation in IO:
import System.Directory (listDirectory)
import Control.Monad (void)
instance RPCToy IO where
mkdir = void . putStrLn . ("better not create "++)
ls = listDirectory
and a little application
import Control.Monad (unless)
demo :: RPCToy m => m ()
demo = do
files <- ls "."
unless ("test" `elem` files) $
mkdir "test"
that can be run in the IO monad
main :: IO ()
main = do
demo
So far so good.
Now suppose that different implementations rely on the same monad m, e.g. from the same networking library. For the typed tagless final style to work distinct monads are needed, here, that are nonetheless essentially the same. The ambiguity can be removed by wrapping things:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
newtype Local a = Local {runLocal :: IO a} deriving (Functor, Applicative, Monad)
and then implement RPCToy Local,
instance RPCToy Local where
mkdir = Local . putStrLn . ("BETTER NOT CREATE "++)
ls = Local . listDirectory
that can be run nicely
main :: IO ()
main = do
runLocal demo
What bugs me is this: The implementers have to put a lot of Locals in their code, or, rather repetitively, wrap the library functions like so
localListDirectory = Local . listDirectory
...
One idea is to create an 'indexed monad' im i a, im i being the monad, that carries around an index type i for the sole purpose of letting the compiler distinguish the different implementations. The RebindableSyntax extension makes this possible without having to give up the do syntax. But each monad needs to be 'lifted' into this indexed monad. The improvement is this: Each monad m and the functions therein need to be lifted only once. Otherwise it's still quite convoluted.
I'm wondering whether there's a nicer way to get rid of the monad wrapping.
Here's one approach: Introduce a monad transformer that just wraps another monad with the twist of adding a phantom type i,
import Control.Monad.Trans.Class (MonadTrans, lift)
newtype IndexedWrapT i m a = IndexedWrapT {runIndexedWrapT :: m a}
deriving (Functor, Applicative, Monad)
instance MonadTrans (IndexedWrapT i) where
lift = IndexedWrapT
The phantom type i has the sole purpose of letting different implementations have distinct type.
Then wrap (lift) the relevant functions once, e.g.:
putStrLn' :: MonadTrans t => String -> t IO ()
putStrLn' = lift . putStrLn
On the implementation side
data MyImpl'
type MyImpl = IndexedWrapT MyImpl' IO
runMyImpl :: MyImpl a -> IO a
runMyImpl = runIndexedWrapT
instance RPCToy MyImpl where
mkdir = putStrLn' . ("BETTER NOT CREATE "++)
....
Having formulated the wrapping operation as a monad transformer it becomes clear that other approaches to composing effects may be used, here, as pointed out in the comments, e.g. freer-simple or polysemy.

How can I compile for ghc-8.4.2 code that uses lenses inside StateT monad

I tried to compile this small program from Gabriel Gonzales 2013 blog post "Program imperatively using Haskell lenses":
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
import Control.Monad.Trans.Class
import Control.Monad.Trans.State
data Game = Game
{ _score :: Int
, _units :: [Unit]
, _boss :: Unit
} deriving (Show)
data Unit = Unit
{ _health :: Int
, _position :: Point
} deriving (Show)
data Point = Point
{ _x :: Double
, _y :: Double
} deriving (Show)
makeLenses ''Game
makeLenses ''Unit
makeLenses ''Point
strike :: StateT Game IO ()
strike = do
lift $ putStrLn "*shink*"
boss.health -= 10
I get error: No instance for (Control.Monad.State.Class.MonadStat Game (StateT Game IO)) arising from a use of ‘-=’
What imports are required these days?
There is more than one version of StateT. To find out which one you need you have to go to the Haddock docs for the problematic function (in this case (-=) and follow the links from its definition.
There are two monad transformer packages, called "mtl" and "transformers", so my first thought was that you might be using the wrong one. However when I followed the links from the (-=) operator I found myself in the adjunctions library, which is also by lens author Edward Kmett.
So to get (-=) and it's relatives, it looks like you have to import Control.Monad.Representable.State from adjunctions.
Edit Looking through the source for Control.Monad.Representable.State it seems to be re-exporting the MonadState class from the transformers library. So make sure that you are using that one rather than the mtl.

How to combine data composition and monad transformers

I'm somewhat new to monad transformers, and currently trying to use a StateT/Except stack in a project. The difficulty I'm having is that I have a few layers of data composition (types with operations on them, contained within types that have other operations on them), and I can't figure out how to elegantly use monad transformers in that design. Concretely, I'm having trouble writing the following code (simplified example, obviously):
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Monad.Except
import Control.Monad.State
import Control.Monad.Trans.Except (Except, throwE)
import Control.Monad.Trans.State (StateT)
data ComposedState = ComposedState { state :: Bool }
data MyError = MyError { message :: String }
-- If the passed in state is true, change it to false; otherwise throw.
throwingModification :: ComposedState -> Except MyError ComposedState
throwingModification (ComposedState True) = return $ ComposedState False
throwingModification _ = throwE $ MyError "error!"
-- A state which composes with #ComposedState#,
data MyState = MyState { composed :: ComposedState }
-- and a monad transformer state to allow me to modify it and propagate
-- errors.
newtype MyMonad a = MyMonad { contents :: StateT MyState (Except MyError) a }
deriving ( Functor
, Applicative
, Monad
, MonadState MyState
, MonadError MyError )
anAction :: MyMonad ()
anAction = do -- want to apply throwingModification to the `composed` member,
-- propogating any exception
undefined
where I have a potentially "throwing" operation on ComposedState, and I want to use that operation in a stateful, throwing operation on MyState. I can obviously do that by deconstructing the whole stack and rebuilding it, but the whole point of the monadic structure is that I shouldn't have to. Is there a terse, idiomatic solution?
Apologies for the lengthy code snippet--I did my best to cut it down.
The more natural way of doing this would be to write throwingModification from the start in the MyMonad monad, like so:
throwingModification' :: MyMonad ()
throwingModification' = do ComposedState flag <- gets composed
if not flag then throwError $ MyError "error!"
else modify (\s -> s { composed = (composed s)
{ Main.state = False } })
I'm assuming here that the composed states contain other components that you want to preserve, which makes the modify clause ugly. Using lenses can make this cleaner.
However, if you're stuck with the current form of throwingModification, you'll probably have to write your own combinator, since the usual State combinators don't include mechanisms for switching the state type s, which is what you're effectively trying to do.
The following definition of usingState may help. It transforms a StateT operation from one state to another using a getter and setter. (Again, a lens approach would be cleaner.)
usingState :: (Monad m) => (s -> t) -> (s -> t -> s)
-> StateT t m a -> StateT s m a
usingState getter setter mt = do
s <- get
StateT . const $ do (a, t) <- runStateT mt (getter s)
return (a, setter s t)
I don't think there's an easy way to modify usingState to work between general MonadState monads instead of directly on a StateT, so you'll need to lift it manually and convert it through your MyMonad data type.
With usingState so defined, you can write the following. (Note >=> comes from Control.Monad.)
MyMonad $ usingState getComposed putComposed $
StateT (throwingModification >=> return . ((),))
with helpers:
getComposed = composed
putComposed s c = s { composed = c }
This is still a little ugly, but that's because the type t -> Except e t must be adapted to StateT (t -> Except e ((), t)), then transformed to the s state by the combinator, and then wrapped manually in your MyMonad, as explained above.
With Lenses
I'm not suggesting lenses are a miracle cure or anything, but they do help clean up a few of the uglier parts of the code.
After adding lenses:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
import Control.Monad ((>=>))
import Control.Monad.Except (Except, MonadError, throwError)
import Control.Monad.State (get, MonadState, runStateT, StateT(..))
data MyError = MyError { _message :: String }
data MyState = MyState { _composed :: ComposedState }
data ComposedState = ComposedState { _state :: Bool }
makeLenses ''ComposedState
makeLenses ''MyError
makeLenses ''MyState
the definition of throwingModification looks a little cleaner:
throwingModification :: ComposedState -> Except MyError ComposedState
throwingModification s =
if s^.state then return $ s&state .~ False
else throwError $ MyError "error!"
and the MyMonad version I gave above certainly benefits:
throwingModification' :: MyMonad ()
throwingModification' = do
flag <- use (composed.state)
if flag then composed.state .= False
else throwError (MyError "error!")
The definition of usingStateL doesn't look much different:
usingStateL :: (Monad m) => Lens' s t -> StateT t m a -> StateT s m a
usingStateL tPart mt = do
s <- get
StateT . const $ do (a, t) <- runStateT mt (s^.tPart)
return (a, s&tPart .~ t)
but it allows the existing lens composed to be used in place of helper functions:
MyMonad $ usingStateL composed $
StateT (throwingModification >=> return . ((),))
and it would generalize to (composed.underneath.state4) if you had complex nested state.
The best solution would be re-write throwingModification as a MyMonad.
throwingModification :: MyMonad ()
throwingModification = do
s <- get
if state s then
put $ ComposedState False
else
throwError $ MyError "error!"
If you can't re-write your function (because it is used elsewhere), you can wrap it instead.

Difficulty with zoom and free monads

I am mucking around with free monads and lens, using the free monad to create my own version of the IO monad:
data MyIO next
= LogMsg String next
| GetInput (String -> next)
deriving (Functor)
I am stacking this on top of a state monad like so: FreeT MyIO (State GameState) a where GameState is:
data GameState = GameState { _players :: [PlayerState] }
Now, what I would like to have is a way to "zoom-into" a PlayerState from a GameState context. Something like this:
zoomPlayer :: Int -> FreeT MyIO (State PlayerState) a -> FreeT MyIO (State GameState) a
zoomPlayer i prog = hoistFreeT (zoom (players . element i)) prog
But I'm getting this error:
No instance for (Data.Monoid.Monoid a1)
arising from a use of ‘_head’
This error seems related to the fact that players . element i is a traversal; if I remove the list aspect from _players and use normal lens then the code works.
Any ideas on how to write this function?
If you are sure you'll never index into a non-existing player and don't mind a little unsafety, you can use the unsafeSingular combinator to turn a Traversal into a Lens, like this:
zoomPlayer :: Int -> FreeT MyIO (State PlayerState) a -> FreeT MyIO (State GameState) a
zoomPlayer i prog = hoistFreeT (zoom (players . unsafeSingular (element i))) prog
(Also, perhaps I would use ix instead of element, but that's unrelated to the problem.)
We can also construct safe indexing lenses for always-infinite sequences, like streams defined using Cofree from the free package:
import Control.Lens (Lens', _Wrapped')
import Control.Comonad.Cofree (Cofree, telescoped)
import Data.Functor.Identity
import Control
sureIx :: Int -> Lens' (Cofree Identity a) a
sureIx i = telescoped $ replicate i _Wrapped'
But a game is unlikely to have infinite players.

"No instance for MonadRandom" when using weightedSample in a monad transformer stack

I want to take a weighted sample from a list inside a monad transformer stack.
I've managed to get this minimal example to type-check, but I don't understand the error message I get upon running main, and I don't know how to fix it.
{-# LANGUAGE NoMonomorphismRestriction, FlexibleContexts #-}
module Testing where
import Control.Monad.IO.Class (liftIO, MonadIO)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State.Lazy (modify, runStateT, StateT)
import Control.Monad.Trans.Reader (ask, runReaderT, ReaderT)
import Data.Random
import Data.Random.Distribution.Uniform.Exclusive (Excludable)
import Data.Random.Shuffle.Weighted (weightedSample)
testS :: [(Int, Int)]
testS = [(1,c) | c <- [11..20]]
loop :: (Num w, Ord w, Show a, MonadIO m, Excludable w, Distribution Uniform w, MonadRandom (StateT [[a]] m)) => ReaderT [(w, a)] (StateT [[a]] m) ()
loop = do
s <- ask
a <- lift $ sample $ weightedSample 1 s
liftIO $ print a
lift $ modify ((:) a)
main :: (MonadIO m, MonadRandom (StateT [[Int]] m)) => m ((), [[Int]])
main = runStateT (runReaderT loop testS) []
The actual program is supposed to help with learning by selecting a random question from the initial config (testS), and then update the weights in the state so that questions the user got wrong become more likely.
Here is the error I get when running main in ghci:
No instance for (random-source-0.3.0.6:Data.Random.Internal.Source.MonadRandom
(Control.Monad.Trans.State.Lazy.StateT [[Int]] m0))
arising from a use of `main'
Possible fix:
add an instance declaration for
(random-source-0.3.0.6:Data.Random.Internal.Source.MonadRandom
(Control.Monad.Trans.State.Lazy.StateT [[Int]] m0))
In the expression: main
In an equation for `it': it = main
I can't seem to get random-fu installed to test, but based on browsing documentation I still have a guess that might be right.
The line
a <- lift $ sample $ weightedSample 1 s
Tries to run sample $ weightedSample 1 s in the underlying monad
StateT [[Int]] IO
However, StateT monads are only MonadRandoms when their state is one of the supported random number generator states.
You probably want to run it in IO, which is itself a MonadRandom.
In other words, add another lift.
BTW if this is right, the reason why it still typechecks initially is that you could in theory add an instance for StateT [[Int]] IO if you wanted. (But you probably don't.)

Resources