diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -3,3 +3,5 @@
 [![CircleCI](https://circleci.com/gh/ejconlon/heart-core/tree/master.svg?style=svg)](https://circleci.com/gh/ejconlon/heart-core/tree/master)
 
 An opinionated library prelude in the UnliftIO style.
+
+`Heart.Core.RIO` contains several definitions from the `RIO` library for compatibility ([LICENSE](https://hackage.haskell.org/package/rio-0.1.14.0/src/LICENSE)).
diff --git a/heart-core.cabal b/heart-core.cabal
--- a/heart-core.cabal
+++ b/heart-core.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: effa1e2d3414d60241a02d0d77c5989b8a5a55d81b56c5ac194f1b2da2e79572
+-- hash: a5cf742adaa5509eb6eec3356fe1c7597afd091efe599b3c5664ecbdd922a276
 
 name:           heart-core
-version:        0.2.0
+version:        0.3.0
 synopsis:       An opinionated library prelude in the UnliftIO style
 description:    Please see the README on GitHub at <https://github.com/ejconlon/heart-core#readme>
 category:       Prelude
diff --git a/src/Heart/Core/RIO.hs b/src/Heart/Core/RIO.hs
--- a/src/Heart/Core/RIO.hs
+++ b/src/Heart/Core/RIO.hs
@@ -2,17 +2,30 @@
 {-# LANGUAGE FunctionalDependencies #-}
 {-# LANGUAGE UndecidableInstances #-}
 
+{- |
+Most definitions follow the RIO lib: https://hackage.haskell.org/package/rio-0.1.14.0/docs/RIO.html
+See LICENSE info in the README.
+-}
 module Heart.Core.RIO
   ( HasStateRef (..)
+  , SomeRef
   , RIO (..)
-  , withRIO
+  , getStateRef
+  , liftRIO
+  , newSomeRef
+  , mapRIO
+  , modifySomeRef
+  , modifyStateRef
+  , putStateRef
   , runRIO
+  , readSomeRef
+  , writeSomeRef
   ) where
 
 import Control.Monad.IO.Unlift (withUnliftIO)
 import Control.Monad.Reader (ReaderT (..))
 import Heart.Core.Prelude
-import UnliftIO.IORef (IORef, readIORef, writeIORef)
+import UnliftIO.IORef (IORef, newIORef, readIORef, writeIORef)
 
 newtype RIO env a = RIO { unRIO :: ReaderT env IO a }
   deriving (Functor, Applicative, Monad, MonadReader env, MonadIO, MonadThrow, MonadFail, MonadCatch, MonadMask)
@@ -20,8 +33,8 @@
 instance MonadUnliftIO (RIO env) where
   askUnliftIO = RIO (ReaderT (\r -> withUnliftIO (\u -> return (UnliftIO (unliftIO u . flip runReaderT r . unRIO)))))
 
-withRIO :: (env -> env') -> RIO env' a -> RIO env a
-withRIO f m = do
+mapRIO :: (env -> env') -> RIO env' a -> RIO env a
+mapRIO f m = do
   env <- ask
   let env' = f env
   runRIO env' m
@@ -34,13 +47,41 @@
 runRIO :: MonadIO m => env -> RIO env a -> m a
 runRIO r m = liftIO (runReaderT (unRIO m) r)
 
+data SomeRef a = SomeRef !(IO a) !(a -> IO ())
+
+readSomeRef :: MonadIO m => SomeRef a -> m a
+readSomeRef (SomeRef x _) = liftIO x
+
+writeSomeRef :: MonadIO m => SomeRef a -> a -> m ()
+writeSomeRef (SomeRef _ x) = liftIO . x
+
+modifySomeRef :: MonadIO m => SomeRef a -> (a -> a) -> m ()
+modifySomeRef (SomeRef read' write) f = liftIO (fmap f read' >>= write)
+
+ioRefToSomeRef :: IORef a -> SomeRef a
+ioRefToSomeRef ref = SomeRef (readIORef ref) (writeIORef ref)
+
+newSomeRef :: MonadIO m => a -> m (SomeRef a)
+newSomeRef = fmap ioRefToSomeRef . newIORef
+
 class HasStateRef st env | env -> st where
-  stateRefL :: Lens' env (IORef st)
+  stateRefL :: Lens' env (SomeRef st)
 
+getStateRef :: (HasStateRef st env, MonadReader env m, MonadIO m) => m st
+getStateRef = do
+  ref <- view stateRefL
+  liftIO (readSomeRef ref)
+
+putStateRef :: (HasStateRef st env, MonadReader env m, MonadIO m) => st -> m ()
+putStateRef st = do
+  ref <- view stateRefL
+  liftIO (writeSomeRef ref st)
+
+modifyStateRef :: (HasStateRef st env, MonadReader env m, MonadIO m) => (st -> st) -> m ()
+modifyStateRef f = do
+  ref <- view stateRefL
+  liftIO (modifySomeRef ref f)
+
 instance HasStateRef st env => MonadState st (RIO env) where
-  get = do
-    ref <- view stateRefL
-    readIORef ref
-  put st = do
-    ref <- view stateRefL
-    writeIORef ref st
+  get = getStateRef
+  put = putStateRef
