packages feed

data-effects-core-0.4.0.0: src/Control/Effect/Interpret.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# OPTIONS_GHC -Wno-unrecognised-pragmas #-}

{-# HLINT ignore "Avoid lambda" #-}

-- SPDX-License-Identifier: MPL-2.0

{- |
Copyright   :  (c) 2025 Sayo contributors
License     :  MPL-2.0 (see the file LICENSE)
Maintainer  :  ymdfield@outlook.jp
-}
module Control.Effect.Interpret where

import Control.Effect (
    Eff (..),
    Free (liftFree),
    hoist,
    runFree,
    type (~>),
    type (~~>),
 )
import Data.Effect (Emb, getEmb)
import Data.Effect.OpenUnion (
    Has,
    In,
    KnownLength,
    KnownOrder,
    Membership,
    Suffix,
    Union,
    hfmapUnion,
    identityMembership,
    keyMembership,
    labelMembership,
    nil,
    project,
    weakens,
    (!++),
    (!:),
    (:>),
    type (++),
 )
import Data.Effect.Tag (unTag)
import Data.Functor.Identity (Identity, runIdentity)

runEff :: (Free c ff, c f) => Eff ff '[Emb f] a -> f a
runEff = runFree (getEmb !: nil) . unEff
{-# INLINE runEff #-}

runPure :: (Free c ff, c Identity) => Eff ff '[] a -> a
runPure = runIdentity . runFree nil . unEff
{-# INLINE runPure #-}

interpret
    :: forall e es ff a c
     . (KnownOrder e, Free c ff)
    => e ~~> Eff ff es
    -> Eff ff (e ': es) a
    -> Eff ff es a
interpret i = interpretAll $ i !: Eff . liftFree
{-# INLINE interpret #-}

reinterpret
    :: forall e es es' ff a c
     . (Suffix es es', KnownOrder e, Free c ff)
    => e ~~> Eff ff es'
    -> Eff ff (e ': es) a
    -> Eff ff es' a
reinterpret i = interpretAll $ i !: Eff . liftFree . weakens
{-# INLINE reinterpret #-}

interprets
    :: forall es r ff a c
     . (KnownLength es, Free c ff)
    => Union es ~~> Eff ff r
    -> Eff ff (es ++ r) a
    -> Eff ff r a
interprets i = interpretAll $ i !++ Eff . liftFree
{-# INLINE interprets #-}

reinterprets
    :: forall es r r' ff a c
     . (Suffix r r', KnownLength es, Free c ff)
    => (Union es (Eff ff r') ~> Eff ff r')
    -> Eff ff (es ++ r) a
    -> Eff ff r' a
reinterprets i = interpretAll $ i !++ Eff . liftFree . weakens @r
{-# INLINE reinterprets #-}

interpose
    :: forall e es ff a c
     . (e :> es, Free c ff)
    => e ~~> Eff ff es
    -> Eff ff es a
    -> Eff ff es a
interpose = interposeFor labelMembership
{-# INLINE interpose #-}

interposeOn
    :: forall key e es ff a c
     . (Has key e es, Free c ff)
    => e ~~> Eff ff es
    -> Eff ff es a
    -> Eff ff es a
interposeOn f = interposeFor (keyMembership @key) (f . unTag)
{-# INLINE interposeOn #-}

interposeIn
    :: forall e es ff a c
     . (e `In` es, Free c ff)
    => e ~~> Eff ff es
    -> Eff ff es a
    -> Eff ff es a
interposeIn = interposeFor identityMembership
{-# INLINE interposeIn #-}

interposeFor
    :: forall e es ff a c
     . (KnownOrder e, Free c ff)
    => Membership e es
    -> e ~~> Eff ff es
    -> Eff ff es a
    -> Eff ff es a
interposeFor i f =
    interpretAll \u ->
        case project i u of
            Just e -> f e
            Nothing -> Eff $ liftFree u
{-# INLINE interposeFor #-}

preinterpose
    :: forall e es ff a c
     . (e :> es, Free c ff)
    => e ~~> Eff ff es
    -> Eff ff es a
    -> Eff ff es a
preinterpose = preinterposeFor labelMembership
{-# INLINE preinterpose #-}

preinterposeOn
    :: forall key e es ff a c
     . (Has key e es, Free c ff)
    => e ~~> Eff ff es
    -> Eff ff es a
    -> Eff ff es a
preinterposeOn f = preinterposeFor (keyMembership @key) (f . unTag)
{-# INLINE preinterposeOn #-}

preinterposeIn
    :: forall e es ff a c
     . (e `In` es, Free c ff)
    => e ~~> Eff ff es
    -> Eff ff es a
    -> Eff ff es a
preinterposeIn = preinterposeFor identityMembership
{-# INLINE preinterposeIn #-}

preinterposeFor
    :: forall e es ff a c
     . (KnownOrder e, Free c ff)
    => Membership e es
    -> e ~~> Eff ff es
    -> Eff ff es a
    -> Eff ff es a
preinterposeFor i f = go
  where
    go :: Eff ff es ~> Eff ff es
    go (Eff a) = Eff $ (`runFree` a) \u ->
        hoist (hfmapUnion go) $ case project i u of
            Just e -> unEff $ f e
            Nothing -> liftFree u
{-# INLINE preinterposeFor #-}

interpretAll
    :: forall es es' ff a c
     . (Free c ff)
    => (Union es ~~> Eff ff es')
    -> Eff ff es a
    -> Eff ff es' a
interpretAll i = go
  where
    go :: Eff ff es ~> Eff ff es'
    go = Eff . runFree (unEff . i . hfmapUnion go) . unEff
{-# INLINE interpretAll #-}

iterAllEff
    :: forall es f ff a c
     . (Free c ff, c f)
    => Union es ~~> f
    -> Eff ff es a
    -> f a
iterAllEff i = go
  where
    go :: Eff ff es ~> f
    go = runFree (i . hfmapUnion go) . unEff
{-# INLINE iterAllEff #-}