haskell mtl/transformers相当于镜头的变焦状态

haskell mtl/transformers equivalent of lens' zooming a state

我正在开发一个基于味噌的网络应用程序,我正在尝试将 Transition Action InnerModel () 的模型(状态)包装到 Transition Action ModelWrapper () 中,其中

type Miso.Transition action model = StateT model (Writer [Sub action])

data ModelWrapper = ClientModel Clients.Model | ...

不幸的是,我似乎找不到修改状态类型的方法,或者根本不确定我必须做什么。

documentation主要展示了如何处理镜头库。到目前为止,我已经将 .= 之类的东西改编成 Control.Monad.State.modify,但我找不到与 zoom 等价的东西,我需要 运行 进行未包装的计算模型为状态。

我已经尝试了以下所有方法,但都没有成功。我得到的最接近的是 execStateT,但我无法保留这些操作,所以它没用。

下面的代码是我尝试解决它的不同方法,可能会提供一些上下文。

updateModel ::
  Action -> 
  Transition Action ModelWrapper ()
updateModel ac = case ac of
  --ShowSection sect -> modify $ \mo -> mo{currentSection=sect}
  --UpdateSubmodel submo -> modify $ \mo -> mo{sectionModel=submo}
  UpdateSubmodel submo -> put submo
  SectionAct sact -> case sact of
    ActionClients clac -> do
      gets $ \(ModelClients mo) -> mo
      (Clients.updateModel sectionPipeback clac)
      --return ()
      --gets (\(ModelClients mo) -> mo)
      --modify ModelClients
      --modify $ \mo -> ModelClients mo
      --ModelClients mo <- get
      --let trans = (Clients.updateModel sectionPipeback clac)
      --    w = execStateT trans mo
      --put $ ModelClients mo
      --let (clmo, acts) = runWriter $ execStateT trans mo
      --let w = execStateT trans mo
      --StateT (ModelClients $ execWriter w) w ()
      --StateT (\ins -> writer )
      --execStateT trans mo
      --execStateT trans mo
      --let (clmo, acts) = runWriter $ execStateT trans mo
      --clmo <- lift $ execStateT trans mo
      --put $ ModelClients clmo
      --lift $ acts
      --pure acts
      --pure $ SeictionAct a
  NoOp -> return ()
lens 中的

zoom 很方便,因为它使用 lens 同时捕获 getter 和 setter。但是如果没有 lens,您可以显式处理 getter 和 setter 并执行相同的操作。添加导入:

import Control.Monad.Trans.Class
import Control.Monad.Trans.State.Strict

然后您可以实现类似 zoom 的函数:

zoomy
  :: Monad m
  => (outer -> inner) -- ^ getter
  -> (inner -> outer -> outer) -- ^ setter
  -> StateT inner m a
  -> StateT outer m a
zoomy getter setter action = do
  origOuter <- get
  (a, newInner) <- lift $ runStateT action (getter origOuter)
  let newOuter = setter newInner origOuter
  put newOuter
  pure a

或者,如果您想直接使用数据构造函数:

zoomier
  :: Monad m
  => (outer -> inner) -- ^ getter
  -> (inner -> outer -> outer) -- ^ setter
  -> StateT inner m a
  -> StateT outer m a
zoomier getter setter (StateT action) = StateT $ \origOuter -> do
  (a, newInner) <- action (getter origOuter)
  let newOuter = setter newInner origOuter
  pure (a, newOuter)