packages feed

shikumi-0.1.0.0: src/Shikumi/Effect/Time.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}

-- | A minimal clock effect for shikumi, modeled on the @Clock@ effect from the
-- third-party @time-effectful@ package. We own this module (rather than depend on
-- @time-effectful@) because shikumi needs a monotonic-clock operation
-- ('getMonotonicTimeNSec') that @time-effectful@'s @Clock@ does not provide, and
-- because owning it keeps the dependency surface unchanged.
--
-- The effect is static-dispatch with side effects: each operation performs real
-- 'IO' under the hood via 'unsafeEff_', so callers need only @Time :> es@ rather
-- than the open-ended @IOE :> es@. The real 'IO' permission is required exactly
-- once, at the discharge site 'runTime', which is where the program edge lives.
module Shikumi.Effect.Time
  ( Time,
    runTime,
    getCurrentTime,
    getMonotonicTimeNSec,
    UTCTime,
  )
where

import Data.Time (UTCTime)
import Data.Time qualified as Time
import Data.Word (Word64)
import Effectful
import Effectful.Dispatch.Static
import GHC.Clock qualified as Clock

data Time :: Effect

type instance DispatchOf Time = 'Static 'WithSideEffects

data instance StaticRep Time = TimeRep

-- | The current wall-clock time (UTC). Used for cache-entry metadata and trace
-- span timestamps.
getCurrentTime :: (Time :> es) => Eff es UTCTime
getCurrentTime = unsafeEff_ Time.getCurrentTime

-- | A monotonic nanosecond counter (never runs backwards), suitable for
-- measuring elapsed time. Used by the evaluation framework to compute per-example
-- latency. Wraps 'GHC.Clock.getMonotonicTimeNSec'.
getMonotonicTimeNSec :: (Time :> es) => Eff es Word64
getMonotonicTimeNSec = unsafeEff_ Clock.getMonotonicTimeNSec

-- | Discharge the 'Time' effect against the real system clock.
runTime :: (IOE :> es) => Eff (Time ': es) a -> Eff es a
runTime = evalStaticRep TimeRep