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 вместо этого, чтобы убедиться, что ваши вычисления работают правильно при работе в многопоточной среде.

Другие вопросы по тегам