Making Read-Only functions for a State in Haskell
haskell, monads
Solution
It's not hard to inject the `Reader` monad into `State`:
read :: Reader s a -> State s a
read a = gets (runReader a)
then you could say
iReadData :: Reader MyState Int
iReadData = do
state <- ask
return (getPieceOf state)
and call it as
x <- read $ iReadData
this would allow you to build up `Reader`s into larger read-only sub-programs and inject them into `State` only where you need to combine them with mutators.
It's not hard to extend this to a `ReaderT` and `StateT` at the top of your monad transformer stack (in fact, the definition above works exactly for this case, just change the type). Extending it to a `ReaderT` and `StateT` in the middle of the stack is harder. You basically need a function
lift1 :: (forall a. m0 a -> m1 a) -> t m0 a -> t m1 a
for every monad transformer `t` in the stack above the `ReaderT`/`StateT`, which isn't part of the standard library.
Problem
I often end up in a situation where it's very convenient to be using the `State` monad, due to having a lot of related functions that need to operate on the same piece of data in a semi-imperative way. Some of the functions need to read the data in the State monad, but will never need to change it. Using the `State` monad as usual in these functions works just fine, but I can't help but feel that I've given up Haskell's inherent safety and replicated a language where any function can mutate anything. Is there some type-level thing that I can do to ensure that these functions can only read from the `State`, and never write to it? Current situation: ``` iWriteData :: Int -> State MyState () iWriteData n = do state <- get put (doSomething n state) -- Ideally this type would show that the state can't change. iReadData :: State MyState Int iReadData = do state <- get return (getPieceOf state) bigFunction :: State MyState () bigFunction = do iWriteData 5 iWriteData 10 num <- iReadData -- How do we know that the state wasn't modified? iWRiteData num ``` Ideally `iReadData` would probably have the type `Reader MyState Int`, but then it doesn't play nicely with the `State`. Having `iReadData` be a regular function seems to be the best bet, but then I have to go through the gymnastics of explicitly extracting and passing it the state every time it's used. What are my options?