packages feed

compaREST-0.1.0.0: src/Data/OpenApi/Compare/Report/Jet.hs

module Data.OpenApi.Compare.Report.Jet
  ( ReportJet (..),
    ReportJet',
    ConstructReportJet (..),
    ReportJetResult,
  )
where

import Control.Applicative
import Control.Monad.Free
import Data.List.NonEmpty
import qualified Data.List.NonEmpty as NE
import Data.OpenUnion
import Data.OpenUnion.Extra
import Data.Typeable
import Text.Pandoc.Builder

-- | A "jet" is a way of simplifying expressions from "outside". The "jetted"
-- expressions should still be completely valid and correct without the jets.
-- Jets just make the expression more "optimized" by identifying patterns and
-- replacing the expressions with "better" ones that have the same sematics.
--
-- The term "jet" in this context was introduced in the Urbit project:
--   https://urbit.org/docs/vere/jetting/
--
-- The pattern fits well for simplifying 'Behavior' tree paths.
class ConstructReportJet x f where
  constructReportJet :: x -> ReportJetResult f (Maybe Inlines)

instance (ConstructReportJet b f, JetArg a) => ConstructReportJet (a -> b) f where
  constructReportJet f = Free (fmap f <$> consumeJetArg @a) >>= constructReportJet

instance ConstructReportJet (Maybe Inlines) f where
  constructReportJet x = Pure x

instance ConstructReportJet Inlines f where
  constructReportJet x = Pure $ Just x

class JetArg a where
  consumeJetArg :: ReportJet' f a

instance Typeable (f a b) => JetArg (f a b) where
  consumeJetArg =
    ReportJet $ \(x :: x) ->
      case eqT @(f a b) @x of
        Nothing -> empty
        Just Refl -> pure $ Pure x

instance TryLiftUnion xs => JetArg (Union xs) where
  consumeJetArg = ReportJet $ fmap Pure . tryLiftUnion

instance JetArg x => JetArg (NonEmpty x) where
  consumeJetArg =
    let (ReportJet f) = (consumeJetArg @x)
     in ReportJet $ \a -> do
          u <- f a
          pure (u >>= \y -> Free $ fmap (NE.cons y) <$> consumeJetArg)
            <|> pure (pure <$> u)

type ReportJetResult f = Free (ReportJet f)

-- Not a true 'Applicative'
newtype ReportJet f x
  = ReportJet (forall a b m. (Typeable (f a b), Alternative m, Monad m) => f a b -> m x)
  deriving stock (Functor)

type ReportJet' f a = ReportJet f (Free (ReportJet f) a)