Zoom экземпляр для ReaderT IORef
Я новичок в объективе и у меня возникли проблемы с реализацией экземпляра Zoom для этого типа:
newtype MyStateT s m a = MyStateT
{ unMyStateT :: ReaderT (IORef s) m a
} deriving (Functor, Applicative, Monad, MonadIO)
instance (MonadIO m) => MonadState s (MyStateT s m) where
get = MyStateT $ ReaderT $ liftIO . readIORef
put x = MyStateT $ ReaderT $ \ref -> liftIO $ writeIORef ref x
Я пытался создать новый IORef с подсостоянием объектива, запустить ReaderT на этом подсостоянии, а затем взять измененное подсостояние и заменить его в основном IORef:
type instance Zoomed (MyStateT s m) = Focusing m
instance (Monad m) => Zoom (MyStateT s m) (MyStateT t m) s t where
zoom l (MyStateT r) =
MyStateT $ ReaderT $ \ref -> do
s <- liftIO $ readIORef ref
ref' <- liftIO $ newIORef $ s ^. l
let v = runReader r ref'
subS' <- liftIO $ readIORef ref'
let s' = s & l .~ subS'
liftIO $ writeIORef ref s'
return v
l
кажется, отличается от обычного объектива, так ^.
а также .~
не компилируется, и я получаю такие ошибки:
• Couldn't match type ‘Focusing m c t’ with ‘Const s t’
Expected type: Getting s t s
Actual type: LensLike' (Zoomed (MyStateT s m) c) t s
• Couldn't match type ‘Focusing m c t’ with ‘Identity t’
Expected type: ASetter t t s s
Actual type: LensLike' (Zoomed (MyStateT s m) c) t s
Может кто-нибудь помочь мне заставить этот экземпляр Zoom работать должным образом? Спасибо!
1 ответ
Мое предложение будет:
type instance Zoomed (MyStateT s m) = Focusing m
instance (MonadIO m) => Zoom (MyStateT s m) (MyStateT t m) s t where
zoom l (MyStateT r) =
MyStateT $ ReaderT $ \ref -> do
s <- liftIO $ readIORef ref
(v', s') <- unfocusing . flip l s $ \t -> Focusing $ do
ref' <- liftIO (newIORef t)
v <- runReaderT r ref'
t' <- liftIO (readIORef ref')
return (v, t')
liftIO $ writeIORef ref s'
return v'
{-# INLINE zoom #-}
Проблема, с которой вы столкнулись (^.)
а также (.~)
что они работают на Lens
, который полиморфен в функторе. Но здесь функтор должен быть Zoomed (MyState s m) c
, который Focusing m c
, Так что вам нужно подать заявку l
непосредственно с помощью функции приложения.
Примечание: вы должны быть немного осторожны с этой реализацией. IORef
не атомарный, если вы не используете atomicModifyIORef
на чистой функции (что не представляется возможным в zoom
). Так что может иметь смысл использовать MVar
с takeMVar
а также putMVar
вместо этого, чтобы убедиться, что ваши вычисления работают правильно при работе в многопоточной среде.