packages feed

heftia-0.1.0.0: src/Control/Freer/Trans.hs

{-# LANGUAGE QuantifiedConstraints #-}

-- 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) 2023 Yamada Ryo
License     :  MPL-2.0 (see the file LICENSE)
Maintainer  :  ymdfield@outlook.jp
Stability   :  experimental
Portability :  portable

A type class to abstract away the encoding details of the Freer carrier transformers.
-}
module Control.Freer.Trans where

import Control.Effect.Class (type (~>))
import Control.Monad.Identity (IdentityT (IdentityT), runIdentityT)
import Data.Free.Sum (pattern L1, pattern R1, type (+))

-- | A type class to abstract away the encoding details of the Freer carrier transformers.
class (forall ins f. c f => c (fr ins f)) => TransFreer c fr | fr -> c where
    {-# MINIMAL liftInsT, liftLowerFT, (hoistFreer, runInterpretF | interpretFT) #-}

    -- | Lift a /instruction/ into a Freer carrier transformer.
    liftInsT :: ins ~> fr ins f

    liftLowerFT :: forall ins f. c f => f ~> fr ins f

    -- | Translate /instruction/s embedded in a Freer carrier transformer.
    transformT :: c f => (ins ~> ins') -> fr ins f ~> fr ins' f
    transformT f = interpretFT liftLowerFT (liftInsT . f)
    {-# INLINE transformT #-}

    -- | Translate an underlying carrier.
    hoistFreer :: (c f, c g) => (f ~> g) -> fr ins f ~> fr ins g
    hoistFreer f = interpretFT (liftLowerFT . f) liftInsT
    {-# INLINE hoistFreer #-}

    interposeLowerT :: (c f, c g) => (f ~> fr ins g) -> fr ins f ~> fr ins g
    interposeLowerT f = interpretFT f liftInsT
    {-# INLINE interposeLowerT #-}

    runInterpretF :: c f => (ins ~> f) -> fr ins f a -> f a
    default runInterpretF :: (c f, c (IdentityT f)) => (ins ~> f) -> fr ins f a -> f a
    runInterpretF f = runIdentityT . interpretFT IdentityT (IdentityT . f)
    {-# INLINE runInterpretF #-}

    interpretFT :: (c f, c g) => (f ~> g) -> (ins ~> g) -> fr ins f ~> g
    interpretFT f i = runInterpretF i . hoistFreer f
    {-# INLINE interpretFT #-}

    reinterpretFT :: c f => (ins ~> fr ins f) -> fr ins f ~> fr ins f
    reinterpretFT = interpretFT liftLowerFT
    {-# INLINE reinterpretFT #-}

mergeFreer ::
    forall fr m ins ins' c.
    (TransFreer c fr, c m) =>
    fr ins (fr ins' m) ~> fr (ins + ins') m
mergeFreer = interpretFT (transformT @c R1) (liftInsT @c . L1)

splitFreer ::
    forall fr m ins ins' c.
    (TransFreer c fr, c m) =>
    fr (ins + ins') m ~> fr ins (fr ins' m)
splitFreer = interpretFT (liftLowerFT . liftLowerFT) \case
    L1 e -> liftInsT e
    R1 e -> liftLowerFT $ liftInsT e