{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE TypeOperators         #-}
{- |
   Module     : Composite.XStep
   License    : MIT
   Stability  : experimental

Tuple functions for composite records, inspired by relude.
-}

module Composite.XStep (
  RSource(runSource)
, RSource'
, XStep
, XStep'
, runSourceI
, runXStep
, prependXStep
) where

import Composite.Record
import Data.Functor.Identity
import Data.Vinyl
import Data.Vinyl.TypeLevel
import Data.Vinyl.XRec

-- | An `RSource` is simply a `ReaderT` transformer. This is a uniquely named
-- type as composte and vinyl aren't compatible via the `IsoHKD` interface due
-- to the way that `(:->)` is defined.
newtype RSource r m a = RSource { runSource :: r -> m a }

-- | The special case where the environment is a `Record`.
type RSource' r = RSource (Record r)

instance Functor m => IsoHKD (RSource r m) (s :-> a) where
  type HKD (RSource r m) (s :-> a) = r -> m a
  unHKD f = RSource $ fmap Val . f
  toHKD (RSource f) = fmap getVal . f

-- | An `XStep` is a n `XRec` with an `RSource`. The type is eta-reduced, but in practice signatures will be written `XStep m a b` to represent a Kleisli arrow from a to m b.
type XStep m a = XRec (RSource a m)

-- | The special case where the environment is a `Record`.
type XStep' m a = XStep m (Record a)

-- | Run an `RSource` against a `Record` and pull out the underlying functor.
runSourceI :: Functor m => RSource r m a -> r -> m (Identity a)
runSourceI x = fmap Identity . runSource x

-- | Run an `XStep` to completion.
runXStep :: (IsoXRec (RSource a m) b, Applicative m) => XStep m a b -> a -> m (Record b)
runXStep x y = rtraverse (`runSourceI` y) (fromXRec x)

-- | The special case where the environnent is a XStep environment is `Record`, and the result should be prepended extensibly.
prependXStep :: (IsoXRec (RSource' a m) b, Applicative m) => XStep' m a b -> Record a -> m (Record (b ++ a))
prependXStep f x = (<+> x) <$> runXStep f x