packages feed

polysemy-scoped-fs-0.1.0.0: src/Polysemy/ScopedReader.hs

{-|
Copyright   : (c) Hisaket VioletRed, 2022
License     : AGPL-3.0-or-later
Maintainer  : hisaket@outlook.jp
Stability   : experimental

This module provides a scoped-reader manner.

In contrast to a normal 'Reader' effect, this manner makes connection of parameter type and effect explicit.

Example\:

>>> import Polysemy ( interpret, runM, embed )
>>> import Polysemy.Output ( Output (Output), output, runOutputSem )

>>> :{
runDebug :: Member (Output String) r => String -> InterpreterFor (ScopedReader String (Output String)) r
runDebug = runScopedReader \i -> interpret \(Output o) -> output $ "[" <> i <> "] " <> o
:}

>>> :{
runM $ runOutputSem (embed . putStrLn) $ runDebug "root" do
    scopedReader $ output "test message 0"
    scopedLocal (<> ".scope-A") do
        scopedReader $ output "test message 1"
        scopedReader $ output "test message 2"
        scopedLocal (<> ".scope-B") do
            scopedReader $ output "test message 3"
:}
[root] test message 0
[root.scope-A] test message 1
[root.scope-A] test message 2
[root.scope-A.scope-B] test message 3

-}

{-# LANGUAGE PartialTypeSignatures #-}
{-# OPTIONS_GHC -Wno-partial-type-signatures #-}

module Polysemy.ScopedReader where

import Polysemy
    ( Member, Sem, raise, raiseUnder, InterpreterFor, transform )
import Polysemy.Scoped.Path ( ScopedP, scopedP_local, runScopedP )
import Polysemy.Scoped.Path.Internal ( interpretH' )
import Control.Category ((>>>))
import Polysemy.Internal ( liftSem )
import Polysemy.Internal.Union
    ( ElemOf (Here), Union (Union), Weaving (Weaving) )


scopedLocal
    ::  ∀i p resource effect r
    .   Member (ScopedReader i effect) r
    =>  (i -> i) -> InterpreterFor (ScopedFix i effect) r
scopedLocal f = scopedFix . scopedP_local f () . raiseUnder

scopedReader :: Member (ScopedReader i effect) r => Sem (effect ': r) a -> Sem r a
scopedReader = scopedEffect

type ScopedReader i effect = ScopedFix i effect

runScopedReader :: (i -> InterpreterFor effect r) -> i -> Sem (ScopedReader i effect ': r) a -> Sem r a
runScopedReader int localEnv = 
    interpretH' \(Weaving e s wv ex ins) ->
        let send' :: _ -> Sem (_ ': _) _
            send' e' =
                liftSem $ Union Here
                    $ Weaving e' s (raise . runScopedReader int localEnv . wv) ex ins
        in case e of
            ScopedEffect e' -> int localEnv $ send' e'
            ScopedFix e' ->
                runScopedP (\() inner -> inner localEnv) (runScopedReader int) $ send' e'


data ScopedFixP p resource effect m a where
    ScopedEffect :: effect m a -> ScopedFixP p resource effect m a
    ScopedFix
        ::  ScopedP p resource (ScopedFixP p resource effect) m a
        ->  ScopedFixP p resource effect m a

type ScopedFix = ScopedFixP ()

scopedEffect :: Member (ScopedFixP p resource effect) r => Sem (effect ': r) a -> Sem r a
scopedEffect = transform ScopedEffect

scopedFix
    ::  Member (ScopedFixP p resource effect) r
    =>  Sem (ScopedP p resource (ScopedFixP p resource effect) ': r) a
    ->  Sem r a
scopedFix = transform ScopedFix