{-|
Module      : Control.Monad.Freer.Time
Description : Effect for working with the current time
Copyright   : (c) Ben Weitzman 2018
License     : MIT
Maintainer  : ben@costarastrolgoy.com
Stability   : experimental
Portability : POSIX
-}

module Control.Monad.Freer.Time
  (currentTime
  ,Time
  ,runTime
  ,runTimeAt
  ,runInstantaneously
  )
where

import Control.Monad.Freer
import Data.Time

-- | The 'Time' effect gives an interface to accessing the current time
data Time a where
  CurrentTime :: Time UTCTime

-- | Get the current time
currentTime :: (Member Time r) => Eff r UTCTime
currentTime = send CurrentTime

-- | Use 'IO' to handle getting the current time
runTime :: Member IO r => Eff (Time ': r) a -> Eff r a
runTime = interpret $ \CurrentTime -> send getCurrentTime

-- | Use a given time that the program will consider "current"
runTimeAt :: UTCTime -> Eff (Time ': r) a -> Eff r a
runTimeAt instant = interpret $ \CurrentTime -> return instant

-- | Use 'IO' get the current time, but make the program think that
-- everything is happening at the same instant. All calls to 'currentTime'
-- will return the same, present-ish 'UTCTime'
runInstantaneously :: (Member IO r) => Eff (Time ': r) a -> Eff r a
runInstantaneously eff = do
  now <- runTime currentTime
  runTimeAt now eff