-- |
-- Module: M.IO.Obs
-- Description: General observer pattern implementation
-- Copyright: (c) axionbuster, 2025
-- License: BSD-3-Clause
--
-- This module provides a general-purpose observer pattern implementation
-- for monitoring and reacting to state changes in STM transactions.
module M.IO.Obs
  ( -- * Observer
    obs,
  )
where

import Control.Monad
import UnliftIO

-- | A general observer that monitors changes in a shared variable and reacts to them.
--
-- The observer watches a target variable for changes, compares values using a custom
-- comparison function, and executes an action when changes are detected.
--
-- @
-- obs targetvar              -- Variable to observe
--     compareandtransform    -- STM function to compare and transform values
--     reacttochange          -- Action to execute when changes occur
-- @
obs ::
  (MonadIO m, Eq a) =>
  -- | Target variable to observe for changes
  TVar a ->
  -- | Function to compare and transform old and new values in STM
  --
  -- Old value is passed first; returned value gets committed and remembered
  (a -> a -> STM a) ->
  -- | Action to execute when changes are detected, with old and new values
  --
  -- Old value is passed first
  (a -> a -> m ()) ->
  m b
obs :: forall (m :: * -> *) a b.
(MonadIO m, Eq a) =>
TVar a -> (a -> a -> STM a) -> (a -> a -> m ()) -> m b
obs TVar a
a a -> a -> STM a
b a -> a -> m ()
c = do
  old <- TVar a -> m a
forall (m :: * -> *) a. MonadIO m => TVar a -> m a
readTVarIO TVar a
a m a -> (a -> m (TVar a)) -> m (TVar a)
forall a b. m a -> (a -> m b) -> m b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= a -> m (TVar a)
forall (m :: * -> *) a. MonadIO m => a -> m (TVar a)
newTVarIO
  forever do
    (ex, ez) <- atomically do
      x <- readTVar old
      y <- readTVar a
      guard (x /= y)
      z <- b x y
      writeTVar old z
      pure (x, z)
    c ex ez