使用带有镜头的单子函数修改状态
Modify state using a monadic function with lenses
我的问题和How to modify using a monadic function with lenses?很相似作者问是否存在这样的问题
overM :: (Monad m) => Lens s t a b -> (a -> m b) -> s -> m t
答案是mapMOf
mapMOf :: Profunctor p =>
Over p (WrappedMonad m) s t a b -> p a (m b) -> s -> m t
我正在尝试使用 monadic 函数实现修改 MonadState
中的状态的函数:
modifyingM :: MonadState s m => ASetter s s a b -> (a -> m b) -> m ()
没有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 ()
这个例子有效。现在我想将代码从 action
提取到通用解决方案 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 ()
这会导致编译时错误:
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
有什么问题?
编辑 1: 分配 new
而不是 old
值。
编辑 2: 添加了无法编译的 @Zeta 解决方案示例。
编辑 3: 删除第二次编辑的示例。由于导入错误,它没有编译(参见 )。
您在 ASetter
上使用 use
,但 use
需要 Getter
:
use :: MonadState s m => Getting a s a -> m a
(.=) :: MonadState s m => ASetter s s a b -> b -> m ()
不幸的是,ASetter
和 Getting
不一样:
type Getting r s a = (a -> Const r a ) -> s -> Const r s
type ASetter s t a b = (a -> Identity b) -> s -> Identity t
我们需要在Const
和Identity
之间任意切换。我们需要 Lens
:
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
注意左边没有f
。接下来,我们注意到您的 lift
不是必需的。毕竟,f
已经在我们的目标 monad m
中工作了;您之前必须使用 lift
,因为 modifyObjects
在 IO
中,而 action
在 StateT Game IO
中,但这里我们只有一个 m
:
modifyingM :: MonadState s m => Lens s s a a -> (a -> m b) -> m ()
modifyingM l f = do
old <- use l
new <- f old
l .= old
行得通!但这可能是错误的,因为您可能想在 l .= old
中设置 new 值。如果是这样,我们必须确保 old
和 new
具有相同的类型:
-- only a here, no b
-- v v v v
modifyingM :: MonadState s m => Lens s s a a -> (a -> m a) -> m ()
modifyingM l f = do
old <- use l
new <- f old
l .= new
请记住,您需要 lift
modifyObjects
不过:
action :: StateT Game IO ()
action = modifyingM objects (lift . modifyObjects)
我们可以就此打住,但为了好玩,让我们再看看 Lens:
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
你给我一个a -> f b
,我就给你一个新的s -> f t
。所以如果我们只是在你的对象中插入一些东西,我们有
> :t \f -> objects f
\f -> objects f
:: Functor f => (GameObject -> f GameObject) -> Game -> f Game
因此,我们只需要一些MonadState s m => (s -> m s) -> m ()
功能,但实现起来很容易:
import Control.Monad.State.Lazy (get, put) -- not the Trans variant!
modifyM :: MonadState s m => (s -> m s) -> m ()
modifyM f = get >>= f >>= put
请注意,您需要使用 mtl
中的 Control.Monad.State
而不是 Control.Monad.Trans.State
。后者仅定义 put :: Monad m => s -> StateT s m ()
和 get :: Monad m => StateT s m s
,但您想使用 mtl
.
的 MonadState
变体
如果我们把所有的东西放在一起,我们看到 modifyingM
可以写成:
modifyingM :: MonadState s m => Lens s s a a -> (a -> m a) -> m ()
modifyingM l f = modifyM (l f)
或者,我们使用可以使用镜头功能,尽管这并没有给我们提供我们可以使用的洞察力 l f
:
modifyingM :: MonadState s m => Lens s s a a -> (a -> m a) -> m ()
modifyingM l f = use l >>= f >>= assign l
我的问题和How to modify using a monadic function with lenses?很相似作者问是否存在这样的问题
overM :: (Monad m) => Lens s t a b -> (a -> m b) -> s -> m t
答案是mapMOf
mapMOf :: Profunctor p =>
Over p (WrappedMonad m) s t a b -> p a (m b) -> s -> m t
我正在尝试使用 monadic 函数实现修改 MonadState
中的状态的函数:
modifyingM :: MonadState s m => ASetter s s a b -> (a -> m b) -> m ()
没有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 ()
这个例子有效。现在我想将代码从 action
提取到通用解决方案 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 ()
这会导致编译时错误:
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
有什么问题?
编辑 1: 分配 new
而不是 old
值。
编辑 2: 添加了无法编译的 @Zeta 解决方案示例。
编辑 3: 删除第二次编辑的示例。由于导入错误,它没有编译(参见
您在 ASetter
上使用 use
,但 use
需要 Getter
:
use :: MonadState s m => Getting a s a -> m a
(.=) :: MonadState s m => ASetter s s a b -> b -> m ()
不幸的是,ASetter
和 Getting
不一样:
type Getting r s a = (a -> Const r a ) -> s -> Const r s
type ASetter s t a b = (a -> Identity b) -> s -> Identity t
我们需要在Const
和Identity
之间任意切换。我们需要 Lens
:
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
注意左边没有f
。接下来,我们注意到您的 lift
不是必需的。毕竟,f
已经在我们的目标 monad m
中工作了;您之前必须使用 lift
,因为 modifyObjects
在 IO
中,而 action
在 StateT Game IO
中,但这里我们只有一个 m
:
modifyingM :: MonadState s m => Lens s s a a -> (a -> m b) -> m ()
modifyingM l f = do
old <- use l
new <- f old
l .= old
行得通!但这可能是错误的,因为您可能想在 l .= old
中设置 new 值。如果是这样,我们必须确保 old
和 new
具有相同的类型:
-- only a here, no b
-- v v v v
modifyingM :: MonadState s m => Lens s s a a -> (a -> m a) -> m ()
modifyingM l f = do
old <- use l
new <- f old
l .= new
请记住,您需要 lift
modifyObjects
不过:
action :: StateT Game IO ()
action = modifyingM objects (lift . modifyObjects)
我们可以就此打住,但为了好玩,让我们再看看 Lens:
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
你给我一个a -> f b
,我就给你一个新的s -> f t
。所以如果我们只是在你的对象中插入一些东西,我们有
> :t \f -> objects f
\f -> objects f
:: Functor f => (GameObject -> f GameObject) -> Game -> f Game
因此,我们只需要一些MonadState s m => (s -> m s) -> m ()
功能,但实现起来很容易:
import Control.Monad.State.Lazy (get, put) -- not the Trans variant!
modifyM :: MonadState s m => (s -> m s) -> m ()
modifyM f = get >>= f >>= put
请注意,您需要使用 mtl
中的 Control.Monad.State
而不是 Control.Monad.Trans.State
。后者仅定义 put :: Monad m => s -> StateT s m ()
和 get :: Monad m => StateT s m s
,但您想使用 mtl
.
MonadState
变体
如果我们把所有的东西放在一起,我们看到 modifyingM
可以写成:
modifyingM :: MonadState s m => Lens s s a a -> (a -> m a) -> m ()
modifyingM l f = modifyM (l f)
或者,我们使用可以使用镜头功能,尽管这并没有给我们提供我们可以使用的洞察力 l f
:
modifyingM :: MonadState s m => Lens s s a a -> (a -> m a) -> m ()
modifyingM l f = use l >>= f >>= assign l