packages feed

polysemy-methodology-composite-0.1.4.0: src/Polysemy/Methodology/Composite.hs

{- |
   Module     : Polysemy.Methodology.Composite
   License    : MIT
   Maintainer : dan.firth@homotopic.tech
   Stability  : experimental

Functions for combining polysemy-methodology with composite.
-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BlockArguments      #-}
{-# LANGUAGE DataKinds           #-}
{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE LambdaCase          #-}
{-# LANGUAGE GADTs               #-}
{-# LANGUAGE PolyKinds           #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications    #-}
{-# LANGUAGE TypeOperators       #-}
module Polysemy.Methodology.Composite (
  runMethodologyRmap
, runCoRecMethodologyAsCases
, runCoRecMethodologyAsCases'
, diffractMethodology
, diffractMethodology'
, runInputCase'
, pickCoRecConstructor
, separateRecInitial
, separateRecInitial'
, stripRecInitial
, endRecInitial
, runRecInitialAsInputCompose
, runRecInitialAsInputCompose'
, separateRecTerminal
, separateRecTerminal'
, stripRecTerminal
, endRecTerminal
, fmapCMethodology
, fmapCMethodology'
) where

import Control.Arrow
import Composite.CoRecord
import Data.Vinyl
import Data.Vinyl.Functor
import Polysemy
import Polysemy.Extra
import Polysemy.Input
import Polysemy.Methodology

-- | Run a `Methodology` between two `Rec`s according to a natural transformation
-- between the interpretation functors.
--
-- @since 0.1.4.0
runMethodologyRmap :: forall f g xs r a. RMap xs =>
                      (forall y. f y -> g y)
                   -> Sem (Methodology (Rec f xs) (Rec g xs) ': r) a
                   -> Sem r a
runMethodologyRmap f = runMethodologyPure (rmap f)
{-# INLINE runMethodologyRmap #-}

-- | Run a `Methodology` from a `CoRec` to an `Input` of `Cases'`. You can then use `Polysemy.Vinyl.separateRecInput` and `Polysemy.Vinyl.stripRecInput`
-- to deal with the cases individually.
--
-- @since 0.1.0.0
runCoRecMethodologyAsCases :: forall f zs c x xs r a.
                              (zs ~ (x ': xs), RecApplicative zs, Members '[Input (Cases' f zs c)] r)
                           => Sem (Methodology (CoRec f zs) c ': r) a
                              -- ^ The Methodology to decompose.
                           -> Sem r a
runCoRecMethodologyAsCases = interpret \case
  Process b -> do
    x <- input @(Cases' f zs c)
    return $ foldCoRec x b
{-# INLINE runCoRecMethodologyAsCases #-}

-- | Reinterpreting version of `runCoRecMethodologyAsCases`.
--
--
-- @since 0.1.0.0
runCoRecMethodologyAsCases' :: forall f zs c x xs r a.
                               (zs ~ (x ': xs), RecApplicative zs)
                            => Sem (Methodology (CoRec f zs) c ': r) a
                               -- ^ The Methodology to decompose.
                            -> Sem (Input (Cases' f zs c) ': r) a
runCoRecMethodologyAsCases' = reinterpret \case
  Process b -> do
    x <- input @(Cases' f zs c)
    return $ foldCoRec x b
{-# INLINE runCoRecMethodologyAsCases' #-}

-- | Diffraction is a combination of a `cutMethodology'`, an `mconcatMethodology'` and a `runCoRecMethodologyAsCases`.
--
-- This effectively allows you to make several ad-hoc constructors for d and fully consumes the second half of the cut.
-- This turns out to be quite a good way to start a simple pipeline.
--
-- @since 0.1.0.0
diffractMethodology :: forall b f zs d x xs r a.
                       (Monoid d, zs ~ (x ': xs)
                      , RecApplicative zs
                      , Members '[ Methodology b [CoRec f zs]
                                 , Input (Cases' f zs d)] r)
                    => Sem (Methodology b d ': r) a
                       -- ^ The Methodology to decompose.
                    -> Sem r a
diffractMethodology = cutMethodology' @b @[CoRec f zs] @d
                  >>> reinterpretUnder mconcatMethodology'
                  >>> reinterpretUnder runCoRecMethodologyAsCases'
                  >>> subsume >>> subsume
{-# INLINE diffractMethodology #-}

-- | Reinterpreting version of `diffractMethodology`.
--
-- @since 0.1.0.0
diffractMethodology' :: forall b f zs d x xs r a.
                        (Monoid d, zs ~ (x ': xs), RecApplicative zs)
                     => Sem (Methodology b d ': r) a
                        -- ^ The Methodology to decompose.
                     -> Sem (Methodology b [CoRec f zs] ': Input (Cases' f zs d) ': r) a
diffractMethodology' = cutMethodology' @b @[CoRec f zs] @d
                   >>> reinterpretUnder mconcatMethodology'
                   >>> reinterpretUnder runCoRecMethodologyAsCases'
{-# INLINE diffractMethodology' #-}

-- | Run a `Case'` using `runInputConst` and a function eliminating the `Case'`.
--
-- @since 0.1.1.0
runInputCase' :: forall b f t r a.
                 (f b -> t)
              -> Sem (Input (Case' f t b) ': r) a
              -> Sem r a
runInputCase' f = runInputConst (Case' f)
{-# INLINE runInputCase' #-}

-- | Take a `Methodology` into a `CoRec` and choose a constructor.
--
-- @since 0.1.4.0
pickCoRecConstructor :: forall x f b xs r a. x ∈ xs =>
                        Sem (Methodology b (CoRec f xs) ': r) a
                     -> Sem (Methodology b (f x) ': r) a
pickCoRecConstructor = cutMethodology'
                   >>> rotateEffects2
                   >>> runMethodologyPure CoVal
{-# INLINE pickCoRecConstructor #-}

-- | Factor a `Methodology` with a `Rec` in the result by a `Methodology` to the first variable.
--
-- @since 0.1.3.0
separateRecInitial :: forall b f x xs r a.
                      Members '[Methodology b (f x), Methodology b (Rec f xs)] r
                   => Sem (Methodology b (Rec f (x ': xs)) ': r) a
                      -- ^ The Methodology to decompose.
                   -> Sem r a
separateRecInitial = interpret \case
  Process b -> do
    k   <- process @b @(f x) b
    k'  <- process @b @(Rec f xs) b
    return $ k :& k'
{-# INLINE separateRecInitial #-}

-- | Reinterpreting version of `separateRecInitial`. This assumes you want to handle
-- the separated case first.
--
-- @since 0.1.3.0
separateRecInitial' :: forall b f x xs r a.
                       Sem (Methodology b (Rec f (x ': xs)) ': r) a
                    -> Sem (Methodology b (f x) ': Methodology b (Rec f xs)': r) a
separateRecInitial' = reinterpret2 \case
  Process b -> do
    k   <- process @b @(f x) b
    k'  <- raise $ process @b @(Rec f xs) b
    return $ k :& k'
{-# INLINE separateRecInitial' #-}

-- | Like `separateRecInitial`, but reinterprets the rest of the `Rec` whilst pushing
-- the separated `Methodology` into the stack. Useful for exhausting the `Rec` and
-- dealing with the cases later.
--
-- @since 0.1.3.0
stripRecInitial :: forall b f x xs r a.
                   Members '[Methodology b (f x)] (Methodology b (Rec f xs) ': r)
                => Sem (Methodology b (Rec f (x ': xs)) ': r) a
                -> Sem (Methodology b (Rec f xs)': r) a
stripRecInitial = reinterpret \case
  Process b -> do
    k   <- process @b @(f x) b
    k'  <- process @b @(Rec f xs) b
    return $ k :& k'
{-# INLINE stripRecInitial #-}

-- | Discard a depleted `Methodology` into a `Rec` by returning `RNil`.
--
-- @since 0.1.3.0
endRecInitial :: Sem (Methodology b (Rec f '[]) ': r) a -> Sem r a
endRecInitial = interpret \case
  Process _ -> return RNil
{-# INLINE endRecInitial #-}

-- | Run a `Methodology` into a `Rec` as an `Input` over a `(->)` functor.
--
-- @since 0.1.3.0
runRecInitialAsInputCompose :: forall b f xs r a. (RMap xs,
                               Members '[ Input (Rec (Compose ((->) b) f) xs)] r)
                            => Sem (Methodology b (Rec f xs) ': r) a
                            -> Sem r a
runRecInitialAsInputCompose = interpret \case
  Process b -> do
    z <- input @(Rec (Compose ((->) b) f) xs)
    return $ rmap (($ b) . getCompose) z
{-# INLINE runRecInitialAsInputCompose #-}

-- | Reinterpreting version of `runRecInitialAsInputCompose`.
--
-- @since 0.1.3.0
runRecInitialAsInputCompose' :: forall b f xs r a. (RMap xs)
                             => Sem (Methodology b (Rec f xs) ': r) a
                             -> Sem (Input (Rec (Compose ((->) b) f) xs) ': r) a
runRecInitialAsInputCompose' = reinterpret \case
  Process b -> do
    z <- input @(Rec (Compose ((->) b) f) xs)
    return $ rmap (($ b) . getCompose) z
{-# INLINE runRecInitialAsInputCompose' #-}

-- | Factor a `Methodology` from a `Rec` by a `Methodology` from the first variable.
--
-- since @0.1.3.0
separateRecTerminal :: forall x c f xs r a. (Monoid c,
                       Members '[Methodology (f x) c, Methodology (Rec f xs) c] r)
                    => Sem (Methodology (Rec f (x ': xs)) c ': r) a
                    -> Sem r a
separateRecTerminal = interpret \case
  Process (b :& bs) -> do
    k   <- process @(f x) b
    k'  <- process @(Rec f xs) @c bs
    return $ k <> k'
{-# INLINE separateRecTerminal #-}

-- | Reinterpreted version of `separateRecTerminal`.
--
-- since @0.1.3.0
separateRecTerminal' :: forall x c f xs r a. Monoid c
                     => Sem (Methodology (Rec f (x ': xs)) c ': r) a
                     -> Sem (Methodology (f x) c ': Methodology (Rec f xs) c ': r) a
separateRecTerminal' = reinterpret2 \case
  Process (b :& bs) -> do
    k   <- process @(f x) b
    k'  <- raise $ process @(Rec f xs) @c bs
    return $ k <> k'
{-# INLINE separateRecTerminal' #-}

-- | Like `separateRecTerminal, but reinterprets the rest of the `Rec` whilst pushing
-- the separated `Methodology` into the stack. Useful for exhausting the `Rec` and
-- dealing with the cases later.
--
-- since @0.1.3.0
stripRecTerminal :: forall x c f xs r a. (Monoid c,
                    Members '[Methodology (f x) c] (Methodology (Rec f xs) c ': r))
                 => Sem (Methodology (Rec f (x ': xs)) c ': r) a
                 -> Sem (Methodology (Rec f xs) c ': r) a
stripRecTerminal = reinterpret \case
  Process (b :& bs) -> do
    k   <- process @(f x) b
    k'  <- process @(Rec f xs) @c bs
    return $ k <> k'
{-# INLINE stripRecTerminal #-}

-- | Discard a depleted `Methodology` fom a `Rec` by returning `mempty`.
--
-- @since 0.1.3.0
endRecTerminal :: Monoid b => Sem (Methodology (Rec f '[]) b ': r) a -> Sem r a
endRecTerminal = interpret \case
  Process _ -> return mempty
{-# INLINE endRecTerminal #-}

-- | Like `fmapMethodology`, but used with a `Compose`d functor to strip the top
-- layer from the functor.
--
-- @since 0.1.4.0
fmapCMethodology :: forall f g h b c r a. Traversable f =>
                     Sem (Methodology ((f :. g) b) ((f :. h) c) ': r) a
                  -> Sem (Methodology (g b) (h c) ': r) a
fmapCMethodology = reinterpret \case
  Process b -> Compose <$> traverse (process @(g b) @(h c)) (getCompose b)
{-# INLINE fmapCMethodology #-}

-- | Reinterpreting version of `fmapCMethodology`.
--
-- @since 0.1.4.0
fmapCMethodology' :: forall f g h b c r a. Traversable f =>
                     Sem (Methodology ((f :. g) b) ((f :. h) c) ': r) a
                  -> Sem (Methodology (g b) (h c) ': r) a
fmapCMethodology' = reinterpret \case
  Process b -> Compose <$> traverse (process @(g b) @(h c)) (getCompose b)
{-# INLINE fmapCMethodology' #-}