Я пытаюсь создать бесплатную монаду (используя free
), которая действует так же, как монада StateT , но который также позволяет запускать монады в базовом состоянии AppState
. У меня есть отдельный конструктор LiftAction
, который содержит эти типы. Идея состоит в том, что вы удерживаете zoom
ing Actions на низком уровне до тех пор, пока они не достигнут AppState, который может хранить различные состояния внутри своей карты расширений.
Вот моя более ранняя (неудачная) попытка использования mtl: Поднять через вложенные преобразователи состояния (mtl)< /а>
В любом случае, поскольку это в основном оболочка над StateT
, я дал ему экземпляр MonadState
, но теперь я работаю над добавлением возможности масштабирования состояния монады с помощью библиотека линз; Я получаю некоторые странные ошибки компилятора, которые мне трудно понять (ошибки объектива обычно не очень удобны для пользователя).
Вот мой код и первая попытка:
{-# language GeneralizedNewtypeDeriving #-}
{-# language DeriveFunctor #-}
{-# language FlexibleInstances #-}
{-# language MultiParamTypeClasses #-}
{-# language RankNTypes #-}
{-# language TypeFamilies #-}
{-# language UndecidableInstances #-}
module Eve.Internal.AppF
( Action(..)
, App
, AppState(..)
, liftAction
, execApp
) where
import Control.Monad.State
import Control.Monad.Free
import Control.Lens
type App a = Action AppState a
data AppState = AppState
{ baseExts :: Int -- Assume this actually contains many nested states which we can zoom
}
data ActionF s next =
LiftAction (Action AppState next)
| LiftIO (IO next)
| StateAction (StateT s IO next)
deriving Functor
newtype Action s a = Action
{ getAction :: Free (ActionF s) a
} deriving (Functor, Applicative, Monad)
liftActionF :: ActionF s next -> Action s next
liftActionF = Action . liftF
instance MonadState s (Action s) where
state = liftActionF . StateAction . state
liftAction :: Action AppState a -> Action s a
liftAction = liftActionF . LiftAction
execApp :: Action AppState a -> StateT AppState IO a
execApp (Action actionF) = foldFree toState actionF
where
toState (LiftAction act) = execApp act
toState (LiftIO io) = liftIO io
toState (StateAction st) = st
type instance Zoomed (Action s) = Zoomed (StateT s IO)
instance Zoom (Action s) (Action t) s t where
zoom l (Action actionF) = Action $ hoistFree (zoomActionF l) actionF
where
zoomActionF _ (LiftAction act) = LiftAction act
zoomActionF _ (LiftIO io) = LiftIO io
zoomActionF lns (StateAction act) = StateAction $ zoom lns act
Я получаю сообщение об ошибке:
/Users/chris/dev/eve/src/Eve/Internal/AppF.hs:53:65: error:
• Couldn't match type ‘a’ with ‘c’
‘a’ is a rigid type variable bound by
a type expected by the context:
forall a. ActionF s a -> ActionF t a
at /Users/chris/dev/eve/src/Eve/Internal/AppF.hs:53:42
‘c’ is a rigid type variable bound by
the type signature for:
zoom :: forall c.
LensLike' (Zoomed (Action s) c) t s -> Action s c -> Action t c
at /Users/chris/dev/eve/src/Eve/Internal/AppF.hs:53:7
Expected type: LensLike'
(Control.Lens.Internal.Zoom.Focusing IO a) t s
Actual type: LensLike' (Zoomed (Action s) c) t s
• In the first argument of ‘zoomActionF’, namely ‘l’
In the first argument of ‘hoistFree’, namely ‘(zoomActionF l)’
In the second argument of ‘($)’, namely
‘hoistFree (zoomActionF l) actionF’
• Relevant bindings include
actionF :: Free (ActionF s) c
(bound at /Users/chris/dev/eve/src/Eve/Internal/AppF.hs:53:22)
l :: LensLike' (Zoomed (Action s) c) t s
(bound at /Users/chris/dev/eve/src/Eve/Internal/AppF.hs:53:12)
zoom :: LensLike' (Zoomed (Action s) c) t s
-> Action s c -> Action t c
(bound at /Users/chris/dev/eve/src/Eve/Internal/AppF.hs:53:7)
Насколько я могу судить, это сбивает с толку, потому что StateT встроен в конструктор Free и теряет след типа a
.
Раньше у меня была рабочая версия, определяя мою собственную функцию масштабирования, которая увеличивала базовый StateT с учетом «объектива», но хитрость в том, что я хотел бы, чтобы это также работало с Traversal'
s, поэтому самым чистым способом было бы написать экземпляр масштабирования .
У кого-нибудь есть идея, как заставить это скомпилировать? Заранее спасибо!! Если возможно, попробуйте скомпилировать свои ответы перед публикацией, спасибо!