Haskell MTL / трансформаторы эквивалентно масштабированию состояния объектива

Я работаю над веб-приложением на основе мисо и пытаюсь обернуть модель (состояние) Transition Action InnerModel () в Transition Action ModelWrapper () где

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

а также data ModelWrapper = ClientModel Clients.Model | ...

К сожалению, я не могу найти способ изменить тип штата или не совсем уверен, что мне делать.

Документация показывает, как работать с библиотекой линз. До сих пор я адаптировал такие вещи, как .= в 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 ()

1 ответ

Решение

zoom от lens это удобно, потому что он использует lens захватить и геттер и сеттер одновременно. Но без lensВы можете явно иметь дело с геттером и сеттером и делать то же самое. Добавление импорта:

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)
Другие вопросы по тегам