packages feed

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

-- 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 an ad-hoc specialized version of
 "Control.Monad.Hefty.Interpret" to accelerate interpretations that have a
single state type @s@, especially for effects like t'Data.Effect.State.State' or
 [@Writer@]("Data.Effect.Writer").
-}
module Control.Monad.Hefty.Interpret.State where

import Control.Effect (type (~>))
import Control.Monad.Hefty.Interpret (qApp)
import Control.Monad.Hefty.Types (Eff (Op, Val), sendUnionBy, sendUnionHBy)
import Data.Effect.OpenUnion.Internal (IsSuffixOf)
import Data.Effect.OpenUnion.Internal.FO (Union, prj, weakens, (!+), type (<|))
import Data.Effect.OpenUnion.Internal.HO (UnionH, hfmapUnion, nilH)
import Data.Kind (Type)

-- | An ad-hoc stateful version of t'Control.Monad.Hefty.Types.Interpreter' for performance.
type StateInterpreter s e m (ans :: Type) = forall x. e x -> s -> (s -> x -> m ans) -> m ans

-- | An ad-hoc stateful version of t'Control.Monad.Hefty.Types.Elaborator' for performance.
type StateElaborator s e m ans = StateInterpreter s (e m) m ans

-- * Interpretation functions

interpretStateBy
    :: forall s e ef ans a
     . s
    -> (s -> a -> Eff '[] ef ans)
    -> StateInterpreter s e (Eff '[] ef) ans
    -> Eff '[] (e ': ef) a
    -> Eff '[] ef ans
interpretStateBy = reinterpretStateBy
{-# INLINE interpretStateBy #-}

reinterpretStateBy
    :: forall s e ef' ef ans a
     . (ef `IsSuffixOf` ef')
    => s
    -> (s -> a -> Eff '[] ef' ans)
    -> StateInterpreter s e (Eff '[] ef') ans
    -> Eff '[] (e ': ef) a
    -> Eff '[] ef' ans
reinterpretStateBy s0 ret hdl =
    iterStateAllEffHFBy s0 ret nilH (hdl !+ \u s k -> sendUnionBy (k s) (weakens u))
{-# INLINE reinterpretStateBy #-}

interpretStateRecWith
    :: forall s e ef eh a
     . s
    -> (forall ans. StateInterpreter s e (Eff eh ef) ans)
    -> Eff eh (e ': ef) a
    -> Eff eh ef a
interpretStateRecWith = reinterpretStateRecWith
{-# INLINE interpretStateRecWith #-}

reinterpretStateRecWith
    :: forall s e ef' ef eh a
     . (ef `IsSuffixOf` ef')
    => s
    -> (forall ans. StateInterpreter s e (Eff eh ef') ans)
    -> Eff eh (e ': ef) a
    -> Eff eh ef' a
reinterpretStateRecWith s0 hdl = loop s0
  where
    loop :: s -> Eff eh (e ': ef) ~> Eff eh ef'
    loop s =
        iterStateAllEffHFBy
            s
            (const pure)
            (\u s' k -> sendUnionHBy (k s') $ hfmapUnion (loop s') u)
            (hdl !+ \u s' k -> sendUnionBy (k s') (weakens u))
{-# INLINE reinterpretStateRecWith #-}

-- * Interposition functions

interposeStateBy
    :: forall s e ef ans a
     . (e <| ef)
    => s
    -> (s -> a -> Eff '[] ef ans)
    -> StateInterpreter s e (Eff '[] ef) ans
    -> Eff '[] ef a
    -> Eff '[] ef ans
interposeStateBy s0 ret f =
    iterStateAllEffHFBy s0 ret nilH \u s k ->
        maybe (sendUnionBy (k s) u) (\e -> f e s k) (prj @e u)
{-# INLINE interposeStateBy #-}

-- * Transformation to monads

iterStateAllEffHFBy
    :: forall s eh ef m ans a
     . (Monad m)
    => s
    -> (s -> a -> m ans)
    -> StateInterpreter s (UnionH eh (Eff eh ef)) m ans
    -> StateInterpreter s (Union ef) m ans
    -> Eff eh ef a
    -> m ans
iterStateAllEffHFBy s0 ret fh ff = loop s0
  where
    loop s = \case
        Val x -> ret s x
        Op u q -> either (`fh` s) (`ff` s) u k
          where
            k s' = loop s' . qApp q
{-# INLINE iterStateAllEffHFBy #-}

-- TODO: add other pattern functions.