delta-store-1.0.0.0: src/Data/Delta/Update.hs
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-|
Copyright : © 2022-2023 IOHK, 2023-2025 Cardano Foundation
License : Apache-2.0
Description : Computations that produce a delta and a result.
'Update' represents a computation which produces a delta and a result.
Similar to the 'Control.Monad.Trans.State.State' monad,
but involves a 'Delta' type.
Useful for composing updates to a 'DBVar', via 'onDBVar'.
Note: This module is preliminary.
-}
module Data.Delta.Update (
-- * Update
-- ** Type
Update
-- ** View
, runUpdate
, applyUpdate
, onDBVar
-- ** Combinators
, nop
, update
, updateWithResult
-- ** Helpers
, updateWithError
, updateWithResultAndError
, updateMany
, updateField
) where
import Prelude
import Data.DBVar
( DBVar
, modifyDBMaybe
)
import Data.Delta
( Delta (..)
)
{-------------------------------------------------------------------------------
Update
Type, View
-------------------------------------------------------------------------------}
-- | A computation which inspects a value @a ~ Base da@
-- and produces a delta @da@ and a result of type @r@.
--
-- Related to the 'Control.Monad.Trans.State.State' monad:
-- The type @'Update' ('Data.Delta.Core.Replace' s) r@ is essentially equivalent to
-- @'Control.Monad.Trans.State.State' s r@.
newtype Update da r = Update { runUpdate_ :: Base da -> (Maybe da, r) }
-- | Run the 'Update' computation.
runUpdate :: (a ~ Base da) => Update da r -> a -> (Maybe da, r)
runUpdate = runUpdate_
-- | Semantics.
applyUpdate
:: (Delta da, a ~ Base da)
=> Update da r -> a -> (a,r)
applyUpdate (Update g) a =
case g a of
(da, r) -> (da `apply` a, r)
-- | Apply an 'Update' to a 'DBVar'.
onDBVar
:: (Monad m, Delta da)
=> DBVar m da -> Update da r -> m r
onDBVar dbvar = modifyDBMaybe dbvar . runUpdate
{-------------------------------------------------------------------------------
Combinators
-------------------------------------------------------------------------------}
-- | Map results.
instance Functor (Update da) where
fmap f (Update g) = Update $ \a ->
case g a of
(da, r) -> (da, f r)
-- | No operation.
--
-- Use the 'Functor' instance, specifically '(<$)'
-- to add results other than '()'.
nop :: Update da ()
nop = Update $ const (Nothing, ())
-- | Compute a delta.
update :: (a ~ Base da) => (a -> da) -> Update da ()
update f = Update $ \a -> (Just (f a), ())
-- | Compute a delta with result.
updateWithResult
:: (a ~ Base da)
=> (a -> (da, r)) -- Delta with result.
-> Update da r
updateWithResult f = Update $ \a ->
case f a of
(da, r) -> (Just da, r)
-- | Compute a delta or fail.
updateWithError
:: (a ~ Base da)
=> (a -> Either e da)
-> Update da (Either e ())
updateWithError f = Update $ \a ->
case f a of
Left e -> (Nothing, Left e)
Right da -> (Just da, Right ())
-- | Compute a delta with result or fail.
updateWithResultAndError
:: (a ~ Base da)
=> (a -> Either e (da, r))
-> Update da (Either e r)
updateWithResultAndError f = Update $ \a ->
case f a of
Left e -> (Nothing, Left e)
Right (da,r) -> (Just da, Right r)
-- | Lift an update for a single delta to a list of deltas.
updateMany
:: Update da r
-> Update [da] r
updateMany (Update g) = Update $ \a ->
case g a of
(Nothing, r) -> (Nothing, r)
(Just da, r) -> (Just [da], r)
{- | Helper function for lifting the 'Update' from a
record field to the record.
Example:
@
data Pair a b = Pair a b
first :: Pair a b -> a
data DeltaPair da db
= UpdateFirst da
| UpdateSecond db
updateField first UpdateFirst
:: (a -> Update da r)
-> (Pair a b -> Update (DeltaPair da db) r)
@
-}
updateField
:: (a ~ Base da, b ~ Base db)
=> (b -> a)
-- ^ View field.
-> (da -> db)
-- ^ Lift delta to
-> Update da r
-> Update db r
updateField view embed (Update g) =
Update $ lift . g . view
where
lift (mda, r) = (embed <$> mda, r)