packages feed

polysemy-db-0.0.1.0: lib/Polysemy/Db/Interpreter/AtomicState.hs

module Polysemy.Db.Interpreter.AtomicState where

import Conc (Lock, lock)
import Polysemy.AtomicState (AtomicState (AtomicGet, AtomicState))

import qualified Polysemy.Db.Effect.Store as Store
import Polysemy.Db.Effect.Store (QStore)

insertState ::
  ∀ d err r .
  Members [QStore Maybe () d !! err, Stop err] r =>
  Sem r d ->
  Sem r d
insertState initial = do
  restop do
    raise initial >>= tap \ d ->
      Store.deleteAll *> Store.insert d

readState ::
  ∀ d err r .
  Members [QStore Maybe () d !! err, Stop err] r =>
  Sem r d ->
  Sem r d
readState initial = do
  stored <- restop (Store.fetch ())
  maybe (insertState @d @err initial) pure stored

handleAtomicStateStore ::
  ∀ tag d err r0 r a .
  Members [QStore Maybe () d !! err, Lock @@ tag, Stop err] r =>
  Sem r d ->
  AtomicState d (Sem r0) a ->
  Sem r a
handleAtomicStateStore initial = \case
  AtomicState f ->
    tag @tag @Lock $ lock do
      (newState, a) <- f <$> raise (readState @d @err initial)
      a <$ insertState @d @err (pure newState)
  AtomicGet ->
    readState @d @err initial

-- |Interpret 'AtomicState' as a singleton table.
--
-- Given an action that produces an initial value, every action reads the value from the database and writes it
-- back.
interpretAtomicStateStore ::
  ∀ tag d err r .
  Members [QStore Maybe () d !! err, Lock @@ tag] r =>
  Sem (Stop err : r) d ->
  InterpreterFor (AtomicState d !! err) r
interpretAtomicStateStore initial =
  interpretResumable (handleAtomicStateStore initial)

-- |Interpret 'AtomicState' as a singleton table.
--
-- Given an initial value, every action reads the value from the database and writes it back.
interpretAtomicStateStoreAs ::
  ∀ tag d err r .
  Members [QStore Maybe () d !! err, Lock @@ tag] r =>
  d ->
  InterpreterFor (AtomicState d !! err) r
interpretAtomicStateStoreAs value =
  interpretAtomicStateStore (pure value)

atomicStateScope ::
  Member (Scoped param (QStore Maybe () d !! err) !! err) r =>
  param ->
  (() -> Sem (QStore Maybe () d !! err : Stop err : r) a) ->
  Sem (Stop err : r) a
atomicStateScope p use =
  restop (scoped p (raiseUnder (use ())))

interpretAtomicStateStoreScoped ::
  ∀ tag param d err r .
  Members [Scoped param (QStore Maybe () d !! err) !! err, Lock @@ tag] r =>
  Sem (Stop err : r) d ->
  InterpreterFor (Scoped param (AtomicState d !! err) !! err) r
interpretAtomicStateStoreScoped initial =
  interpretScopedRWith @'[QStore Maybe () d !! err] atomicStateScope \ () ->
    handleAtomicStateStore (insertAt @1 initial)

interpretAtomicStateStoreScopedAs ::
  ∀ tag param d err r .
  Members [Scoped param (QStore Maybe () d !! err) !! err, Lock @@ tag] r =>
  d ->
  InterpreterFor (Scoped param (AtomicState d !! err) !! err) r
interpretAtomicStateStoreScopedAs value =
  interpretAtomicStateStoreScoped (pure value)

interpretAtomicStatesStore ::
  ∀ tag param d err r .
  Members [QStore Maybe () d !! err, Scoped param (QStore Maybe () d !! err) !! err, Lock @@ tag] r =>
  Sem (Stop err : r) d ->
  InterpretersFor [AtomicState d !! err, Scoped param (AtomicState d !! err) !! err] r
interpretAtomicStatesStore initial =
  interpretAtomicStateStoreScoped initial .
  interpretAtomicStateStore (raiseUnder initial)