packages feed

heftia-effects-0.1.0.0: src/Control/Effect/Handler/Heftia/Writer.hs

{-# LANGUAGE AllowAmbiguousTypes #-}

-- 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

Interpreter and elaborator for the t'Control.Effect.Class.Writer.Writer' effect class.
See [README.md](https://github.com/sayo-hs/heftia/blob/master/README.md).
-}
module Control.Effect.Handler.Heftia.Writer where

import Control.Effect.Class (type (~>))
import Control.Effect.Class.Writer (Tell (tell), TellI (Tell), WriterS (Censor, Listen))
import Control.Effect.Freer (Fre, intercept, interposeT, interpretK, interpretT, type (<|))
import Control.Monad.Trans.Writer.CPS (WriterT, runWriterT)
import Control.Monad.Trans.Writer.CPS qualified as T
import Data.Function ((&))
import Data.Tuple (swap)

elaborateWriterT ::
    forall w m es.
    (Monad m, Monoid w, TellI w <| es) =>
    WriterS w (Fre es m) ~> Fre es m
elaborateWriterT = \case
    Listen m -> listenT m
    Censor f m -> m & intercept @(TellI w) \(Tell w) -> Tell $ f w

elaborateWriterTransactionalT ::
    forall w m es.
    (Monad m, Monoid w, TellI w <| es) =>
    WriterS w (Fre es m) ~> Fre es m
elaborateWriterTransactionalT = \case
    Listen m -> listenT m
    Censor f m -> do
        (a, w) <- confiscateT m
        tell $ f w
        pure a

listenT ::
    (Monoid w, Monad m, TellI w <| es) =>
    Fre es m a ->
    Fre es m (a, w)
listenT m = do
    (a, w) <- confiscateT m
    tell w
    pure (a, w)
{-# INLINE listenT #-}

confiscateT ::
    forall w m es a.
    (Monoid w, Monad m, TellI w <| es) =>
    Fre es m a ->
    Fre es m (a, w)
confiscateT = runWriterT . interposeT @(TellI w) \(Tell w) -> T.tell w
{-# INLINE confiscateT #-}

interpretTell :: (Monad m, Monoid w) => Fre (TellI w ': es) m a -> Fre es m (w, a)
interpretTell = fmap swap . runWriterT . interpretTellT
{-# INLINE interpretTell #-}

interpretTellT :: (Monad m, Monoid w) => Fre (TellI w ': es) m a -> WriterT w (Fre es m) a
interpretTellT = interpretT \(Tell w) -> T.tell w
{-# INLINE interpretTellT #-}

interpretTellK :: (Monad m, Monoid w) => Fre (TellI w ': es) m a -> Fre es m (w, a)
interpretTellK =
    interpretK (pure . (mempty,)) \k (Tell w) -> do
        (w', r) <- k ()
        pure (w <> w', r)