我的问题与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:删除第二次修改的示例。由于导入错误,它没有编译(参见comment)。
答案 0 :(得分:6)
您在use
上使用ASetter
,但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
有效!但这可能是错误的,因为您可能想在old
中设置 new 值。如果是这种情况,我们必须确保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)
:
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t
我们可以在这里停下来,但为了一些乐趣,让我们再看看Lens:
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
函数,但这很容易实现:
Control.Monad.State
请注意,您需要使用mtl
中的Control.Monad.Trans.State
而不是put :: Monad m => s -> StateT s m ()
。后者仅定义get :: Monad m => StateT s m s
和MonadState
,但您希望使用mtl
中的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
:
$("li").click(function() {
$("li").removeClass("selected bold");
var id = $(this).attr('id');
$(document).find('[data-parents="'+id+'"]').addClass("bold");
$(document).find('[data-children="'+id+'"]').addClass("bold");
$(this).addClass("selected");
});