mikelpr
mikelpr

Reputation: 63

haskell mtl/transformers equivalent of lens' zooming a state

I'm working on a miso-based webapp and I'm trying to wrap the model (state) of a Transition Action InnerModel () into a Transition Action ModelWrapper () where

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

and data ModelWrapper = ClientModel Clients.Model | ...

unfortunately I can't seem to find a way to modify the state's type, or am not sure at all what I have to do.

The documentation shows how to do deal mostly with the lens library. So far I've adapted things like .= into Control.Monad.State.modify, but I can't find an equivalent to zoom, which I need to for running a computation with the unwrapped model as state.

I've tried all of the following with no luck. the closest i got was with execStateT, but I couldn't keep the actions so it was useless.

The code below has my different attempts to tackle it and may provide some context.

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 ()

Upvotes: 2

Views: 236

Answers (1)

Michael Snoyman
Michael Snoyman

Reputation: 31335

zoom from lens is convenient because it uses a lens to capture both a getter and setter at the same time. But without a lens, you can explicitly deal with a getter and setter and do the same thing. Adding the imports:

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

You can then implement a zoom-like function:

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

Or, if you want to play directly with the data constructors:

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)

Upvotes: 2

Related Questions