My question is quite similar to How to modify using a monadic function with lenses? The author asked if something like this exists
overM :: (Monad m) => Lens s t a b -> (a -> m b) -> s -> m t
The answer was mapMOf
mapMOf :: Profunctor p =>
Over p (WrappedMonad m) s t a b -> p a (m b) -> s -> m t
I'm trying to implement a function that modifies state in MonadState
using a monadic function:
modifyingM :: MonadState s m => ASetter s s a b -> (a -> m b) -> m ()
Example without modifingM
:
{-# LANGUAGE TemplateHaskell #-}
module Main where
import Control.Lens (makeLenses, use, (.=))
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State.Lazy (StateT(StateT), execStateT)
data GameObject = GameObject
{ _num :: Int
} deriving (Show)
data Game = Game
{ _objects :: [GameObject]
} deriving (Show)
makeLenses ''Game
makeLenses ''GameObject
defaultGame = Game {_objects = map GameObject [0 .. 3]}
action :: StateT Game IO ()
action = do
old <- use objects
new <- lift $ modifyObjects old
objects .= new
modifyObjects :: [GameObject] -> IO [GameObject]
modifyObjects objs = return objs -- do modifications
main :: IO ()
main = do
execStateT action defaultGame
return ()
This example works. Now I'd like to extract the code from action
to a generic solution modifingM
:
{-# LANGUAGE TemplateHaskell #-}
module Main where
import Control.Lens (makeLenses, use, (.=), ASetter)
import Control.Monad.State.Class (MonadState)
import Control.Monad.Trans.Class (lift)
import Control.Monad.Trans.State.Lazy (StateT(StateT), execStateT)
data GameObject = GameObject
{ _num :: Int
} deriving (Show)
data Game = Game
{ _objects :: [GameObject]
} deriving (Show)
makeLenses ''Game
makeLenses ''GameObject
defaultGame = Game {_objects = map GameObject [0 .. 3]}
modifyingM :: MonadState s m => ASetter s s a b -> (a -> m b) -> m ()
modifyingM l f = do
old <- use l
new <- lift $ f old
l .= new
action :: StateT Game IO ()
action = modifyingM objects modifyObjects
modifyObjects :: [GameObject] -> IO [GameObject]
modifyObjects objs = return objs -- do modifications
main :: IO ()
main = do
execStateT action defaultGame
return ()
This results in compile time errors:
Main.hs:26:14: error:
• Couldn't match type ‘Data.Functor.Identity.Identity s’
with ‘Data.Functor.Const.Const a s’
Expected type: Control.Lens.Getter.Getting a s a
Actual type: ASetter s s a b
• In the first argument of ‘use’, namely ‘l’
In a stmt of a 'do' block: old <- use l
In the expression:
do { old <- use l;
new <- lift $ f old;
l .= new }
• Relevant bindings include
f :: a -> m b (bound at app/Main.hs:25:14)
l :: ASetter s s a b (bound at app/Main.hs:25:12)
modifyingM :: ASetter s s a b -> (a -> m b) -> m ()
(bound at app/Main.hs:25:1)
Main.hs:31:10: error:
• Couldn't match type ‘IO’ with ‘StateT Game IO’
Expected type: StateT Game IO ()
Actual type: IO ()
• In the expression: modifyingM objects modifyObjects
In an equation for ‘action’:
action = modifyingM objects modifyObjects
What's the problem?
Edit 1: Assign new
instead of old
value.
Edit 2: Added example with solution of @Zeta that does not compile.
Edit 3: Remove example of second edit. It didn't compile due to wrong imports (see comment).