I've written (almost) zoom2
many times before---I usually call it getsM
. A generic version looks like
-- from Control.Monad.State
gets :: MonadState s m => (s -> a) -> m a
getsM :: MonadState s m => (s -> m a) -> m a
getsM f = state $ \s -> (,s) <$> f s
Or if you don't mind fixing a particular stack
-- from Control.Monad.Trans.State
gets :: Monad m => (s -> a) -> StateT s m a
getsM :: Monad m => (s -> m a) -> StateT s m a
getsM f = StateT $ \s -> (,s) <$> f s