packages feed

data-effects-0.4.0.0: src/Data/Effect/Reader.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE UndecidableInstances #-}

-- SPDX-License-Identifier: MPL-2.0

{- |
Copyright   :  (c) 2023-2025 Sayo contributors
License     :  MPL-2.0 (see the file LICENSE)
Maintainer  :  ymdfield@outlook.jp

Effects that can be used to hold environmental values in the context.
Environmental values are immutable and do not change across procedures, but you
can modify the value within a local scope using the `local` operation.
-}
module Data.Effect.Reader (
    module Data.Effect.Reader,
    Ask (..),
    Local (..),
)
where

import Control.Effect (sendFor)
import Control.Effect.Interpret (interposeFor)
import Data.Effect (Ask (Ask), Local (Local))
import Data.Effect.OpenUnion (IdentityResolver, Membership, membership)

makeEffectF_' (def & noGenerateLabel & noGenerateOrderInstance) ''Ask
makeEffectH_' (def & noGenerateLabel & noGenerateOrderInstance) ''Local

-- | Obtains a value from the environment and returns it transformed by the given function.
asks
    :: forall r es ff a c
     . (Ask r :> es, Functor (Eff ff es), Free c ff)
    => (r -> a)
    -> Eff ff es a
asks f = f <$> ask
{-# INLINE asks #-}

-- | Interpret the t'Ask'/t'Local' effects.
runReader
    :: forall r es ff a c
     . (forall es'. Applicative (Eff ff es'), Free c ff)
    => r
    -> Eff ff (Local r ': Ask r ': es) a
    -> Eff ff es a
runReader r = runAsk r . runLocal
{-# INLINE runReader #-}

-- | Interpret the t'Ask' effect.
runAsk
    :: forall r es ff a c
     . (Applicative (Eff ff es), Free c ff)
    => r
    -> Eff ff (Ask r ': es) a
    -> Eff ff es a
runAsk r = interpret \Ask -> pure r
{-# INLINE runAsk #-}

-- | Interpret the t'Local' effect.
runLocal
    :: forall r es ff a c
     . (Applicative (Eff ff es), Ask r `In` es, Free c ff)
    => Eff ff (Local r ': es) a
    -> Eff ff es a
runLocal = interpret handleLocal
{-# INLINE runLocal #-}

-- | A handler for the t'Local' effect.
handleLocal
    :: forall r es ff c
     . (Applicative (Eff ff es), Ask r `In` es, Free c ff)
    => Local r ~~> Eff ff es
handleLocal = handleLocalFor $ membership @IdentityResolver
{-# INLINE handleLocal #-}

-- | A handler for the t'Local' effect.
handleLocalFor
    :: forall r es ff c
     . (Applicative (Eff ff es), Free c ff)
    => Membership (Ask r) es
    -> Local r ~~> Eff ff es
handleLocalFor pr (Local f a) = a & interposeFor pr \Ask -> f <$> sendFor pr Ask
{-# INLINE handleLocalFor #-}