packages feed

heftia-0.4.0.0: src/Control/Monad/Hefty/Interpret.hs

{-# LANGUAGE AllowAmbiguousTypes #-}

-- SPDX-License-Identifier: MPL-2.0 AND BSD-3-Clause

-- This Source Code Form is subject to the terms of the Mozilla Public
-- License, v. 2.0. If a copy of the MPL was not distributed with this
-- file, You can obtain one at https://mozilla.org/MPL/2.0/.

{- |
Copyright   :  (c) 2016 Allele Dev; 2017 Ixperta Solutions s.r.o.; 2017 Alexis King; 2024 Sayo Koyoneda
License     :  MPL-2.0 (see the LICENSE file) AND BSD-3-Clause (see the LICENSE.BSD3 file)
Maintainer  :  ymdfield@outlook.jp

This module provides functions for interpretation.
Please refer to the documentation of the [top-level module]("Control.Monad.Hefty").
-}
module Control.Monad.Hefty.Interpret where

import Control.Arrow ((>>>))
import Control.Effect (type (~>))
import Control.Monad.Hefty.Types (
    Eff (Op, Val),
    Elaborator,
    Interpreter,
    sendUnionBy,
    sendUnionHBy,
    type (~~>),
 )
import Data.Effect.HFunctor (HFunctor, hfmap)
import Data.Effect.OpenUnion.Internal (IsSuffixOf, WeakenN)
import Data.Effect.OpenUnion.Internal.FO (
    Member (prj),
    Union,
    extract,
    nil,
    weakenN,
    weakens,
    (!+),
    type (<|),
 )
import Data.Effect.OpenUnion.Internal.HO (
    MemberH (prjH),
    UnionH,
    extractH,
    hfmapUnion,
    nilH,
    weakenNH,
    weakensH,
    (!!+),
    type (<<|),
 )
import Data.FTCQueue (FTCQueue, ViewL (TOne, (:|)), tviewl, (><))

-- * Running t`Eff`

-- | Lowers the computation into a monad @m@ by treating the effect as a monad.
runEff :: (Monad m) => Eff '[] '[m] ~> m
runEff = iterEffBy pure $ stateless id
{-# INLINE runEff #-}

-- | Extracts the value from a computation that contains only pure values without any effect.
runPure :: Eff '[] '[] a -> a
runPure = \case
    Val x -> x
    Op u _ -> case u of
        Left u' -> nilH u'
        Right u' -> nil u'
{-# INLINE runPure #-}

-- * Standard interpretation functions

-- ** For first-order effects

-- | Interprets the first-order effect @e@ at the head of the list using the provided natural transformation style handler.
interpret
    :: forall e ef eh
     . (e ~> Eff eh ef)
    -- ^ Effect handler
    -> Eff eh (e ': ef) ~> Eff eh ef
interpret = reinterpret
{-# INLINE interpret #-}

-- | Interprets the first-order effect @e@ at the head of the list using the provided continuational stateful handler.
interpretWith
    :: forall e ef a
     . Interpreter e (Eff '[] ef) a
    -- ^ Effect handler
    -> Eff '[] (e ': ef) a
    -> Eff '[] ef a
interpretWith = reinterpretWith
{-# INLINE interpretWith #-}

-- | Interprets the first-order effect @e@ at the head of the list using the provided value handler and continuational stateful handler.
interpretBy
    :: forall e ef ans a
     . (a -> Eff '[] ef ans)
    -- ^ Value handler
    -> Interpreter e (Eff '[] ef) ans
    -- ^ Effect handler
    -> Eff '[] (e ': ef) a
    -> Eff '[] ef ans
interpretBy = reinterpretBy
{-# INLINE interpretBy #-}

{- | Interprets the first-order effect @e@ at the head of the list using the provided continuational stateful handler.

Interpretation is performed recursively with respect to the scopes of unelaborated higher-order effects @eh@.
Note that during interpretation, the continuational state is reset (delimited) and does not persist beyond scopes.
-}
interpretRecWith
    :: forall e ef eh a
     . (forall ans. Interpreter e (Eff eh ef) ans)
    -- ^ Effect handler
    -> Eff eh (e ': ef) a
    -> Eff eh ef a
interpretRecWith = reinterpretRecWith
{-# INLINE interpretRecWith #-}

-- ** For higher-order effects

-- | Interprets the higher-order effect @e@ at the head of the list using the provided natural transformation style elaborator.
interpretH
    :: forall e eh ef
     . (HFunctor e)
    => e ~~> Eff eh ef
    -- ^ Effect elaborator
    -> Eff (e ': eh) ef ~> Eff eh ef
interpretH = reinterpretH
{-# INLINE interpretH #-}

-- | Interprets the single higher-order effect @e@ using the provided continuational stateful elaborator.
interpretHWith
    :: forall e eh ef a
     . (HFunctor e)
    => Interpreter (e (Eff '[e] ef)) (Eff eh ef) a
    -- ^ Effect elaborator
    -> Eff '[e] ef a
    -> Eff eh ef a
interpretHWith = reinterpretHWith
{-# INLINE interpretHWith #-}

-- | Interprets the single higher-order effect @e@ using the provided value handler and continuational stateful elaborator.
interpretHBy
    :: forall e eh ef ans a
     . (HFunctor e)
    => (a -> Eff eh ef ans)
    -- ^ Value handler
    -> Interpreter (e (Eff '[e] ef)) (Eff eh ef) ans
    -- ^ Effect elaborator
    -> Eff '[e] ef a
    -> Eff eh ef ans
interpretHBy = reinterpretHBy
{-# INLINE interpretHBy #-}

{- | Interprets the higher-order effect @e@ at the head of the list using the provided continuational stateful elaborator.

Interpretation is performed recursively with respect to the scopes of unelaborated higher-order effects @eh@.
Note that during interpretation, the continuational state is reset (delimited) and does not persist beyond scopes.
-}
interpretRecHWith
    :: forall e eh ef a
     . (HFunctor e)
    => (forall ans. Elaborator e (Eff eh ef) ans)
    -- ^ Effect elaborator
    -> Eff (e ': eh) ef a
    -> Eff eh ef a
interpretRecHWith = reinterpretRecHWith
{-# INLINE interpretRecHWith #-}

-- * Reinterpretation functions

-- ** For first-order effects

reinterpret
    :: forall e ef' ef eh
     . (ef `IsSuffixOf` ef')
    => (e ~> Eff eh ef')
    -> Eff eh (e ': ef) ~> Eff eh ef'
reinterpret f = reinterpretRecWith (stateless f)
{-# INLINE reinterpret #-}

reinterpretN
    :: forall n e ef' ef eh
     . (WeakenN n ef ef')
    => (e ~> Eff eh ef')
    -> Eff eh (e ': ef) ~> Eff eh ef'
reinterpretN f = reinterpretRecNWith @n (stateless f)
{-# INLINE reinterpretN #-}

reinterpretWith
    :: forall e ef' ef a
     . (ef `IsSuffixOf` ef')
    => Interpreter e (Eff '[] ef') a
    -> Eff '[] (e ': ef) a
    -> Eff '[] ef' a
reinterpretWith = reinterpretBy pure
{-# INLINE reinterpretWith #-}

reinterpretNWith
    :: forall n e ef' ef a
     . (WeakenN n ef ef')
    => Interpreter e (Eff '[] ef') a
    -> Eff '[] (e ': ef) a
    -> Eff '[] ef' a
reinterpretNWith = reinterpretNBy @n pure
{-# INLINE reinterpretNWith #-}

reinterpretBy
    :: forall e ef' ef ans a
     . (ef `IsSuffixOf` ef')
    => (a -> Eff '[] ef' ans)
    -> Interpreter e (Eff '[] ef') ans
    -> Eff '[] (e ': ef) a
    -> Eff '[] ef' ans
reinterpretBy ret hdl = iterAllEffHFBy ret nilH (hdl !+ flip sendUnionBy . weakens)
{-# INLINE reinterpretBy #-}

reinterpretNBy
    :: forall n e ef' ef ans a
     . (WeakenN n ef ef')
    => (a -> Eff '[] ef' ans)
    -> Interpreter e (Eff '[] ef') ans
    -> Eff '[] (e ': ef) a
    -> Eff '[] ef' ans
reinterpretNBy ret hdl = iterAllEffHFBy ret nilH (hdl !+ flip sendUnionBy . weakenN @n)
{-# INLINE reinterpretNBy #-}

reinterpretRecWith
    :: forall e ef' ef eh a
     . (ef `IsSuffixOf` ef')
    => (forall ans. Interpreter e (Eff eh ef') ans)
    -> Eff eh (e ': ef) a
    -> Eff eh ef' a
reinterpretRecWith hdl = loop
  where
    loop :: Eff eh (e ': ef) ~> Eff eh ef'
    loop = iterAllEffHFBy pure (flip sendUnionHBy . hfmapUnion loop) (hdl !+ flip sendUnionBy . weakens)
{-# INLINE reinterpretRecWith #-}

reinterpretRecNWith
    :: forall n e ef' ef eh a
     . (WeakenN n ef ef')
    => (forall ans. Interpreter e (Eff eh ef') ans)
    -> Eff eh (e ': ef) a
    -> Eff eh ef' a
reinterpretRecNWith hdl = loop
  where
    loop :: Eff eh (e ': ef) ~> Eff eh ef'
    loop = iterAllEffHFBy pure (flip sendUnionHBy . hfmapUnion loop) (hdl !+ flip sendUnionBy . weakenN @n)
{-# INLINE reinterpretRecNWith #-}

-- ** For higher-order effects

reinterpretH
    :: forall e eh eh' ef
     . (HFunctor e, eh `IsSuffixOf` eh')
    => e ~~> Eff eh' ef
    -> Eff (e ': eh) ef ~> Eff eh' ef
reinterpretH elb = reinterpretRecHWith (stateless elb)
{-# INLINE reinterpretH #-}

reinterpretNH
    :: forall n e eh eh' ef
     . (HFunctor e, WeakenN n eh eh')
    => e ~~> Eff eh' ef
    -> Eff (e ': eh) ef ~> Eff eh' ef
reinterpretNH elb = reinterpretRecNHWith @n (stateless elb)
{-# INLINE reinterpretNH #-}

reinterpretHWith
    :: forall e eh ef a
     . (HFunctor e)
    => Interpreter (e (Eff '[e] ef)) (Eff eh ef) a
    -> Eff '[e] ef a
    -> Eff eh ef a
reinterpretHWith = reinterpretHBy pure
{-# INLINE reinterpretHWith #-}

reinterpretNHWith
    :: forall n e eh ef a
     . (HFunctor e, WeakenN n '[] eh)
    => Interpreter (e (Eff '[e] ef)) (Eff eh ef) a
    -> Eff '[e] ef a
    -> Eff eh ef a
reinterpretNHWith = reinterpretHWith
{-# INLINE reinterpretNHWith #-}

reinterpretHBy
    :: forall e eh ef ans a
     . (HFunctor e)
    => (a -> Eff eh ef ans)
    -> Interpreter (e (Eff '[e] ef)) (Eff eh ef) ans
    -> Eff '[e] ef a
    -> Eff eh ef ans
reinterpretHBy ret elb = iterAllEffHFBy ret (elb . extractH) (flip sendUnionBy)
{-# INLINE reinterpretHBy #-}

reinterpretNHBy
    :: forall n e eh ef ans a
     . (HFunctor e, WeakenN n '[] eh)
    => (a -> Eff eh ef ans)
    -> Interpreter (e (Eff '[e] ef)) (Eff eh ef) ans
    -> Eff '[e] ef a
    -> Eff eh ef ans
reinterpretNHBy = reinterpretHBy
{-# INLINE reinterpretNHBy #-}

reinterpretRecHWith
    :: forall e eh eh' ef a
     . (HFunctor e, eh `IsSuffixOf` eh')
    => (forall ans. Elaborator e (Eff eh' ef) ans)
    -> Eff (e ': eh) ef a
    -> Eff eh' ef a
reinterpretRecHWith elb = loop
  where
    loop :: Eff (e ': eh) ef ~> Eff eh' ef
    loop =
        iterAllEffHFBy
            pure
            (elb . hfmap loop !!+ flip sendUnionHBy . weakensH . hfmapUnion loop)
            (flip sendUnionBy)
{-# INLINE reinterpretRecHWith #-}

reinterpretRecNHWith
    :: forall n e eh eh' ef a
     . (HFunctor e, WeakenN n eh eh')
    => (forall ans. Elaborator e (Eff eh' ef) ans)
    -> Eff (e ': eh) ef a
    -> Eff eh' ef a
reinterpretRecNHWith elb = loop
  where
    loop :: Eff (e ': eh) ef ~> Eff eh' ef
    loop =
        iterAllEffHFBy
            pure
            (elb . hfmap loop !!+ flip sendUnionHBy . weakenNH @n . hfmapUnion loop)
            (flip sendUnionBy)
{-# INLINE reinterpretRecNHWith #-}

-- * Interposition functions

-- ** For first-order effects

{- | Reinterprets (hooks) the first-order effect @e@ in the list using the provided natural transformation style handler.

If multiple instances of @e@ exist in the list, the one closest to the head (with the smallest index) will be targeted.
-}
interpose
    :: forall e ef eh
     . (e <| ef)
    => (e ~> Eff eh ef)
    -- ^ Effect handler
    -> Eff eh ef ~> Eff eh ef
interpose f = interposeRecWith (stateless f)
{-# INLINE interpose #-}

{- | Reinterprets (hooks) the first-order effect @e@ in the list using the provided continuational stateful handler.

If multiple instances of @e@ exist in the list, the one closest to the head (with the smallest index) will be targeted.
-}
interposeWith
    :: forall e ef a
     . (e <| ef)
    => Interpreter e (Eff '[] ef) a
    -- ^ Effect handler
    -> Eff '[] ef a
    -> Eff '[] ef a
interposeWith = interposeBy pure
{-# INLINE interposeWith #-}

{- | Reinterprets (hooks) the first-order effect @e@ in the list using the provided value handler and continuational stateful handler.

If multiple instances of @e@ exist in the list, the one closest to the head (with the smallest index) will be targeted.
-}
interposeBy
    :: forall e ef ans a
     . (e <| ef)
    => (a -> Eff '[] ef ans)
    -- ^ Value handler
    -> Interpreter e (Eff '[] ef) ans
    -- ^ Effect handler
    -> Eff '[] ef a
    -> Eff '[] ef ans
interposeBy ret f = iterAllEffHFBy ret nilH \u -> maybe (`sendUnionBy` u) f (prj @e u)
{-# INLINE interposeBy #-}

{- | Reinterprets (hooks) the first-order effect @e@ in the list using the provided continuational stateful handler.

Interpretation is performed recursively with respect to the scopes of unelaborated higher-order effects @eh@.
Note that during interpretation, the continuational state is reset (delimited) and does not persist beyond scopes.

If multiple instances of @e@ exist in the list, the one closest to the head (with the smallest index) will be targeted.
-}
interposeRecWith
    :: forall e ef eh a
     . (e <| ef)
    => (forall ans. Interpreter e (Eff eh ef) ans)
    -- ^ Effect handler
    -> Eff eh ef a
    -> Eff eh ef a
interposeRecWith f = loop
  where
    loop :: Eff eh ef ~> Eff eh ef
    loop = iterAllEffHFBy
        pure
        (flip sendUnionHBy . hfmapUnion loop)
        \u -> maybe (`sendUnionBy` u) f (prj @e u)
{-# INLINE interposeRecWith #-}

-- ** For higher-order effects

{- | Reinterprets (hooks) the higher-order effect @e@ in the list using the provided natural transformation style elaborator.

If multiple instances of @e@ exist in the list, the one closest to the head (with the smallest index) will be targeted.
-}
interposeH
    :: forall e eh ef
     . (e <<| eh, HFunctor e)
    => e ~~> Eff eh ef
    -- ^ Effect elaborator
    -> Eff eh ef ~> Eff eh ef
interposeH f = interposeRecHWith (stateless f)
{-# INLINE interposeH #-}

{- | Reinterprets (hooks) the higher-order effect @e@ in the list using the provided continuational stateful elaborator.

Interpretation is performed recursively with respect to the scopes of unelaborated higher-order effects @eh@.
Note that during interpretation, the continuational state is reset (delimited) and does not persist beyond scopes.

If multiple instances of @e@ exist in the list, the one closest to the head (with the smallest index) will be targeted.
-}
interposeRecHWith
    :: forall e eh ef a
     . (e <<| eh, HFunctor e)
    => (forall ans. Elaborator e (Eff eh ef) ans)
    -- ^ Effect elaborator
    -> Eff eh ef a
    -> Eff eh ef a
interposeRecHWith f = loop
  where
    loop :: Eff eh ef ~> Eff eh ef
    loop =
        iterAllEffHFBy
            pure
            (hfmapUnion loop >>> \u -> maybe (`sendUnionHBy` u) f (prjH @e u))
            (flip sendUnionBy)
{-# INLINE interposeRecHWith #-}

-- * Transformation to monads

-- | Traverses a computation containing only a single first-order effect @e@ using the provided value handler and continuational stateful handler, transforming it into a monad @m@.
iterEffBy
    :: forall e m ans a
     . (Monad m)
    => (a -> m ans)
    -- ^ Value handler
    -> Interpreter e m ans
    -- ^ Effect handler
    -> Eff '[] '[e] a
    -> m ans
iterEffBy ret hdl = iterAllEffHFBy ret nilH (hdl . extract)
{-# INLINE iterEffBy #-}

-- | Traverses a computation containing only a single higher-order effect @e@ using the provided value handler and continuational stateful elaborator, transforming it into a monad @m@.
iterEffHBy
    :: forall e m ans a
     . (Monad m, HFunctor e)
    => (a -> m ans)
    -- ^ Value handler
    -> Interpreter (e (Eff '[e] '[])) m ans
    -- ^ Effect handler
    -> Eff '[e] '[] a
    -> m ans
iterEffHBy ret elb = iterAllEffHFBy ret (elb . extractH) nil
{-# INLINE iterEffHBy #-}

{- | Traverses a computation containing only a single higher-order effect @e@ using the provided natural transformation elaborator, transforming it into a monad @m@.

Traversal is performed recursively with respect to the scope of the higher-order effect @e@.
Note that during traversal, the continuational state is reset (delimited) and does not persist beyond scopes.
-}
iterEffRecH
    :: forall e m
     . (Monad m, HFunctor e)
    => e ~~> m
    -- ^ Effect elaborator
    -> Eff '[e] '[] ~> m
iterEffRecH elb = iterEffRecHWith $ stateless elb
{-# INLINE iterEffRecH #-}

{- | Traverses a computation containing only a single higher-order effect @e@ using the provided continuational stateful elaborator, transforming it into a monad @m@.

Traversal is performed recursively with respect to the scope of the higher-order effect @e@.
Note that during traversal, the continuational state is reset (delimited) and does not persist beyond scopes.
-}
iterEffRecHWith
    :: forall e m
     . (Monad m, HFunctor e)
    => (forall ans. Elaborator e m ans)
    -- ^ Effect elaborator
    -> Eff '[e] '[] ~> m
iterEffRecHWith elb = loop
  where
    loop :: Eff '[e] '[] ~> m
    loop = iterEffHBy pure (elb . hfmap loop)
{-# INLINE iterEffRecHWith #-}

{- | Traverses a computation containing only higher-order effects @eh@ and first-order effects @ef@ using the provided continuational stateful elaborator, transforming it into a monad @m@.

Traversal is performed recursively with respect to the scopes of higher-order effects.
Note that during traversal, the continuational state is reset (delimited) and does not persist beyond scopes.
-}
iterEffRecHFWith
    :: forall eh ef m
     . (Monad m, HFunctor eh)
    => (forall ans. Elaborator eh m ans)
    -- ^ Effect elaborator
    -> (forall ans. Interpreter ef m ans)
    -- ^ Effect handler
    -> Eff '[eh] '[ef] ~> m
iterEffRecHFWith fh ff = loop
  where
    loop :: Eff '[eh] '[ef] ~> m
    loop = iterEffHFBy pure (fh . hfmap loop) ff
{-# INLINE iterEffRecHFWith #-}

{- | Traverses a computation containing only higher-order effects @eh@ and first-order effects @ef@ using the provided value handler,
continuational stateful elaborator, and handler, transforming it into a monad @m@.
-}
iterEffHFBy
    :: forall eh ef m ans a
     . (Monad m, HFunctor eh)
    => (a -> m ans)
    -- ^ Value handler
    -> Interpreter (eh (Eff '[eh] '[ef])) m ans
    -- ^ Effect elaborator
    -> Interpreter ef m ans
    -- ^ Effect handler
    -> Eff '[eh] '[ef] a
    -> m ans
iterEffHFBy ret elb hdl = iterAllEffHFBy ret (elb . extractH) (hdl . extract)
{-# INLINE iterEffHFBy #-}

-- | Traverses all effects using the provided value handler, continuational stateful elaborator, and handler, transforming them into a monad @m@.
iterAllEffHFBy
    :: forall eh ef m ans a
     . (Monad m)
    => (a -> m ans)
    -- ^ Value handler
    -> Interpreter (UnionH eh (Eff eh ef)) m ans
    -- ^ Effect elaborator
    -> Interpreter (Union ef) m ans
    -- ^ Effect handler
    -> Eff eh ef a
    -> m ans
iterAllEffHFBy ret fh ff = loop
  where
    loop = \case
        Val x -> ret x
        Op u q -> either fh ff u k
          where
            k = loop . qApp q
{-# INLINE iterAllEffHFBy #-}

-- * Utilities

-- | Lifts a natural transformation into a continuational stateful interpreter.
stateless :: forall e m ans. (Monad m) => (e ~> m) -> Interpreter e m ans
stateless i e k = i e >>= k
{-# INLINE stateless #-}

-- | Applies a value to a Kleisli arrow in 'FTCQueue' representation.
qApp :: FTCQueue (Eff eh ef) a b -> a -> Eff eh ef b
qApp q' x = case tviewl q' of
    TOne k -> k x
    k :| t -> case k x of
        Val y -> qApp t y
        Op u q -> Op u (q >< t)