Skip to content
Snippets Groups Projects
Commit 66503392 authored by Jens Nolte's avatar Jens Nolte
Browse files

Implement DeltaObservable instance for ObservableHashMap

parent 8e93ca86
No related branches found
No related tags found
No related merge requests found
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
module Qd.Observable.ObservableHashMap ( module Qd.Observable.ObservableHashMap (
ObservableHashMap, ObservableHashMap,
create, new,
observeKey, observeKey,
insert, insert,
delete, delete,
...@@ -10,78 +10,155 @@ module Qd.Observable.ObservableHashMap ( ...@@ -10,78 +10,155 @@ module Qd.Observable.ObservableHashMap (
) where ) where
import Qd.Observable import Qd.Observable
import Qd.Observable.Delta
import Qd.Prelude hiding (lookup, lookupDelete) import Qd.Prelude hiding (lookup, lookupDelete)
import Qd.Utils.GetT import Qd.Utils.GetT
import Control.Concurrent.MVar import Control.Concurrent.MVar
import qualified Data.HashMap.Strict as HM import qualified Data.HashMap.Strict as HM
import Data.Maybe (isJust)
import Data.Unique import Data.Unique
import Language.Haskell.TH.Syntax (mkName, nameBase)
import Lens.Micro.Platform
newtype ObservableHashMap k v = ObservableHashMap (MVar (HM.HashMap k (ObservableValue v)))
data ObservableValue v = ObservableValue { newtype ObservableHashMap k v = ObservableHashMap (MVar (Handle k v))
data Handle k v = Handle {
keyHandles :: HM.HashMap k (KeyHandle v),
subscribers :: HM.HashMap Unique (ObservableMessage (HM.HashMap k v) -> IO ()),
deltaSubscribers :: HM.HashMap Unique (Delta k v -> IO ())
}
data KeyHandle v = KeyHandle {
value :: Maybe v, value :: Maybe v,
subscribers :: (HM.HashMap Unique (ObservableMessage (Maybe v) -> IO ())) keySubscribers :: (HM.HashMap Unique (ObservableMessage (Maybe v) -> IO ()))
} }
modifyValue :: forall k v a. (Eq k, Hashable k) => (ObservableValue v -> IO (ObservableValue v, a)) -> k -> ObservableHashMap k v -> IO a makeLensesWith (lensField .~ (\_ _ -> pure . TopName . mkName . ("_" <>) . nameBase) $ lensRules) ''Handle
modifyValue f k (ObservableHashMap mvar) = modifyMVar mvar $ \hashmap -> runGetT (HM.alterF update k hashmap) makeLensesWith (lensField .~ (\_ _ -> pure . TopName . mkName . ("_" <>) . nameBase) $ lensRules) ''KeyHandle
instance Gettable (HM.HashMap k v) (ObservableHashMap k v) where
getValue (ObservableHashMap mvar) = HM.mapMaybe value . keyHandles <$> readMVar mvar
instance Observable (HM.HashMap k v) (ObservableHashMap k v) where
subscribe ohm callback = modifyHandle update ohm
where
update :: Handle k v -> IO (Handle k v, SubscriptionHandle)
update handle = do
callback (Current, toHashMap handle)
unique <- newUnique
let handle' = handle & set (_subscribers . at unique) (Just callback)
return (handle', SubscriptionHandle $ unsubscribe unique)
unsubscribe :: Unique -> IO ()
unsubscribe unique = modifyHandle_ (return . set (_subscribers . at unique) Nothing) ohm
instance DeltaObservable k v (ObservableHashMap k v) where
subscribeDelta ohm callback = modifyHandle update ohm
where
update :: Handle k v -> IO (Handle k v, SubscriptionHandle)
update handle = do
callback (Reset $ toHashMap handle)
unique <- newUnique
let handle' = handle & set (_deltaSubscribers . at unique) (Just callback)
return (handle', SubscriptionHandle $ unsubscribe unique)
unsubscribe :: Unique -> IO ()
unsubscribe unique = modifyHandle_ (return . set (_deltaSubscribers . at unique) Nothing) ohm
-- TODO
--subscribeAbstraction :: SomeIndexedLens -> (a -> v) -> (IO (a, r) -> IO r) -> (v -> IO ()) -> IO r
--subscribeAbstraction setter getCurrent modifyMVar callback = modify $ do
toHashMap :: Handle k v -> HM.HashMap k v
toHashMap = HM.mapMaybe value . keyHandles
modifyHandle :: (Handle k v -> IO (Handle k v, a)) -> ObservableHashMap k v -> IO a
modifyHandle f (ObservableHashMap mvar) = modifyMVar mvar f
modifyHandle_ :: (Handle k v -> IO (Handle k v)) -> ObservableHashMap k v -> IO ()
modifyHandle_ f = modifyHandle (fmap (,()) . f)
modifyKeyHandle :: (Eq k, Hashable k) => (KeyHandle v -> IO (KeyHandle v, a)) -> k -> ObservableHashMap k v -> IO a
modifyKeyHandle f k = modifyHandle (updateKeyHandle f k)
modifyKeyHandle_ :: forall k v. (Eq k, Hashable k) => (KeyHandle v -> IO (KeyHandle v)) -> k -> ObservableHashMap k v -> IO ()
modifyKeyHandle_ f = modifyKeyHandle (fmap (,()) . f)
updateKeyHandle :: forall k v a. (Eq k, Hashable k) => (KeyHandle v -> IO (KeyHandle v, a)) -> k -> Handle k v -> IO (Handle k v, a)
updateKeyHandle f k = runGetT . (_keyHandles (HM.alterF updateMaybe k))
where where
update :: Maybe (ObservableValue v) -> GetT a IO (Maybe (ObservableValue v)) updateMaybe :: Maybe (KeyHandle v) -> GetT a IO (Maybe (KeyHandle v))
update = fmap toMaybe . (GetT . f) . fromMaybe emptyObservableValue updateMaybe = fmap toMaybe . (GetT . f) . fromMaybe emptyKeyHandle
emptyObservableValue :: ObservableValue v emptyKeyHandle :: KeyHandle v
emptyObservableValue = ObservableValue Nothing HM.empty emptyKeyHandle = KeyHandle Nothing HM.empty
toMaybe :: ObservableValue v -> Maybe (ObservableValue v) toMaybe :: KeyHandle v -> Maybe (KeyHandle v)
toMaybe (ObservableValue Nothing (HM.null -> True)) = Nothing toMaybe (KeyHandle Nothing (HM.null -> True)) = Nothing
toMaybe ov = Just ov toMaybe keyHandle = Just keyHandle
modifyKeyHandleNotifying :: (Eq k, Hashable k) => (KeyHandle v -> IO (KeyHandle v, (Maybe (Delta k v), a))) -> k -> ObservableHashMap k v -> IO a
modifyKeyHandleNotifying f k = modifyHandle $ \handle -> do
(newHandle, (delta, result)) <- updateKeyHandle f k handle
notifySubscribers newHandle delta
return (newHandle, result)
modifyKeyHandleNotifying_ :: (Eq k, Hashable k) => (KeyHandle v -> IO (KeyHandle v, Maybe (Delta k v))) -> k -> ObservableHashMap k v -> IO ()
modifyKeyHandleNotifying_ f k = modifyHandle_ $ \handle -> do
(newHandle, delta) <- updateKeyHandle f k handle
notifySubscribers newHandle delta
return newHandle
modifyValue_ :: forall k v. (Eq k, Hashable k) => (ObservableValue v -> IO (ObservableValue v)) -> k -> ObservableHashMap k v -> IO () notifySubscribers :: Handle k v -> Maybe (Delta k v) -> IO ()
modifyValue_ f = modifyValue (fmap (,()) . f) notifySubscribers _ Nothing = return ()
notifySubscribers handle@Handle{deltaSubscribers, subscribers} (Just delta) = do
mapM_ ($ delta) $ HM.elems deltaSubscribers
mapM_ ($ (Update, toHashMap handle)) $ HM.elems subscribers
modifySubscribers :: (HM.HashMap Unique (ObservableMessage (Maybe v) -> IO ()) -> HM.HashMap Unique (ObservableMessage (Maybe v) -> IO ())) -> ObservableValue v -> ObservableValue v modifyKeySubscribers :: (HM.HashMap Unique (ObservableMessage (Maybe v) -> IO ()) -> HM.HashMap Unique (ObservableMessage (Maybe v) -> IO ())) -> KeyHandle v -> KeyHandle v
modifySubscribers f ov@ObservableValue{subscribers} = ov{subscribers=f subscribers} modifyKeySubscribers f = over _keySubscribers f
create :: IO (ObservableHashMap k v) new :: IO (ObservableHashMap k v)
create = ObservableHashMap <$> newMVar HM.empty new = ObservableHashMap <$> newMVar Handle{keyHandles=HM.empty, subscribers=HM.empty, deltaSubscribers=HM.empty}
observeKey :: forall k v. (Eq k, Hashable k) => k -> ObservableHashMap k v -> SomeObservable (Maybe v) observeKey :: forall k v. (Eq k, Hashable k) => k -> ObservableHashMap k v -> SomeObservable (Maybe v)
observeKey key om@(ObservableHashMap mvar) = SomeObservable FnObservable{getValueFn, subscribeFn} observeKey key ohm@(ObservableHashMap mvar) = SomeObservable FnObservable{getValueFn, subscribeFn}
where where
getValueFn :: IO (Maybe v) getValueFn :: IO (Maybe v)
getValueFn = (value <=< HM.lookup key) <$> readMVar mvar getValueFn = join . preview (_keyHandles . at key . _Just . _value) <$> readMVar mvar
subscribeFn :: ((ObservableMessage (Maybe v) -> IO ()) -> IO SubscriptionHandle) subscribeFn :: ((ObservableMessage (Maybe v) -> IO ()) -> IO SubscriptionHandle)
subscribeFn callback = do subscribeFn callback = do
subscriptionKey <- newUnique subscriptionKey <- newUnique
modifyValue_ (subscribeFn' subscriptionKey) key om modifyKeyHandle_ (subscribeFn' subscriptionKey) key ohm
return $ SubscriptionHandle $ unsubscribe subscriptionKey return $ SubscriptionHandle $ unsubscribe subscriptionKey
where where
subscribeFn' :: Unique -> ObservableValue v -> IO (ObservableValue v) subscribeFn' :: Unique -> KeyHandle v -> IO (KeyHandle v)
subscribeFn' subKey ov@ObservableValue{value} = do subscribeFn' subKey keyHandle@KeyHandle{value} = do
callback (Current, value) callback (Current, value)
return $ modifySubscribers (HM.insert subKey callback) ov return $ modifyKeySubscribers (HM.insert subKey callback) keyHandle
unsubscribe :: Unique -> IO () unsubscribe :: Unique -> IO ()
unsubscribe subKey = modifyValue_ (return . modifySubscribers (HM.delete subKey)) key om unsubscribe subKey = modifyKeyHandle_ (return . modifyKeySubscribers (HM.delete subKey)) key ohm
insert :: forall k v. (Eq k, Hashable k) => k -> v -> ObservableHashMap k v -> IO () insert :: forall k v. (Eq k, Hashable k) => k -> v -> ObservableHashMap k v -> IO ()
insert key value = modifyValue_ fn key insert key value = modifyKeyHandleNotifying_ fn key
where where
fn :: ObservableValue v -> IO (ObservableValue v) fn :: KeyHandle v -> IO (KeyHandle v, Maybe (Delta k v))
fn ov@ObservableValue{subscribers} = do fn keyHandle@KeyHandle{value=oldValue, keySubscribers} = do
mapM_ ($ (Update, Just value)) $ HM.elems subscribers mapM_ ($ (Update, Just value)) $ HM.elems keySubscribers
return ov{value=Just value} let delta = if isJust oldValue then Change key value else Add key value
return (keyHandle{value=Just value}, Just delta)
delete :: forall k v. (Eq k, Hashable k) => k -> ObservableHashMap k v -> IO () delete :: forall k v. (Eq k, Hashable k) => k -> ObservableHashMap k v -> IO ()
delete = modifyValue_ fn delete key = modifyKeyHandleNotifying_ fn key
where where
fn :: ObservableValue v -> IO (ObservableValue v) fn :: KeyHandle v -> IO (KeyHandle v, Maybe (Delta k v))
fn ov@ObservableValue{subscribers} = do fn keyHandle@KeyHandle{value=oldValue, keySubscribers} = do
mapM_ ($ (Update, Nothing)) $ HM.elems subscribers mapM_ ($ (Update, Nothing)) $ HM.elems keySubscribers
return ov{value=Nothing} let delta = if isJust oldValue then Just (Remove key) else Nothing
return (keyHandle{value=Nothing}, delta)
lookupDelete :: forall k v. (Eq k, Hashable k) => k -> ObservableHashMap k v -> IO (Maybe v) lookupDelete :: forall k v. (Eq k, Hashable k) => k -> ObservableHashMap k v -> IO (Maybe v)
lookupDelete = modifyValue fn lookupDelete key = modifyKeyHandleNotifying fn key
where where
fn :: ObservableValue v -> IO (ObservableValue v, Maybe v) fn :: KeyHandle v -> IO (KeyHandle v, (Maybe (Delta k v), Maybe v))
fn ov@ObservableValue{value=oldValue, subscribers} = do fn keyHandle@KeyHandle{value=oldValue, keySubscribers} = do
mapM_ ($ (Update, Nothing)) $ HM.elems subscribers mapM_ ($ (Update, Nothing)) $ HM.elems keySubscribers
return (ov{value=Nothing}, oldValue) let delta = if isJust oldValue then Just (Remove key) else Nothing
return (keyHandle{value=Nothing}, (delta, oldValue))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment