plexus-synapse-3.5.0: src/Synapse/Algebra/Walk.hs
-- | Schema tree walking via recursion schemes
--
-- This module provides schema traversal using proper categorical machinery:
--
-- = Anamorphism (Unfold)
--
-- The schema tree is built via anamorphism - we have a coalgebra that
-- produces one layer of 'SchemaF' from a path seed:
--
-- @
-- coalgebra :: Path -> SynapseM (SchemaF Path)
-- @
--
-- Iterating this coalgebra builds the full tree:
--
-- @
-- unfoldSchema :: Path -> SynapseM SchemaTree
-- unfoldSchema = anaM coalgebra
-- @
--
-- = Catamorphism (Fold)
--
-- To process the tree, we use algebras. A method-collecting algebra:
--
-- @
-- methodAlgebra :: SchemaF [MethodInfo] -> [MethodInfo]
-- methodAlgebra (PluginF schema path children) =
-- localMethods ++ concat children
-- methodAlgebra (MethodF method ns path) =
-- [MethodInfo method path ns]
-- @
--
-- = Hylomorphism (Unfold then Fold)
--
-- Most operations combine unfold and fold. Using 'hyloM' we get fusion:
--
-- @
-- walkMethods :: Path -> SynapseM [MethodInfo]
-- walkMethods = hyloM methodAlgebra schemaCoalgebra
-- @
--
-- The fused version never materializes the intermediate tree!
module Synapse.Algebra.Walk
( -- * Recursion Schemes
walkMethods
, walkSchema
, walkSchemaPar
, foldMethods
-- * Building Trees
, unfoldSchema
, foldSchema
-- * Types
, MethodInfo(..)
, SchemaF(..)
, SchemaTree
-- * Re-exports for algebras
, Fix(..)
) where
import Data.Text (Text)
import Synapse.Schema.Types (Path, PluginSchema(..), MethodSchema(..), ChildSummary(..))
import Synapse.Schema.Functor (SchemaF(..), Fix(..), SchemaTree)
import Synapse.Monad
import Synapse.Algebra.Recursion (hyloM, hyloMPar, unfoldSchema, foldSchema, walkSchemaPar)
import Synapse.Transport (fetchSchemaAt)
-- | Information about a method in context
data MethodInfo = MethodInfo
{ miMethod :: MethodSchema -- ^ The method schema
, miPath :: Path -- ^ Full path to this method
, miNamespace :: Text -- ^ Parent namespace
}
deriving stock (Show, Eq)
-- ============================================================================
-- Algebras
-- ============================================================================
-- | Algebra for collecting methods
--
-- This is a proper F-algebra: SchemaF [MethodInfo] -> [MethodInfo]
-- It shows how to combine child results with local data.
methodAlgebra :: SchemaF [MethodInfo] -> [MethodInfo]
methodAlgebra (PluginF schema path childResults) =
-- Local methods from this plugin
let localMethods =
[ MethodInfo m (path ++ [methodName m]) (psNamespace schema)
| m <- psMethods schema
]
-- Combine with results from children (already processed)
in localMethods ++ concat childResults
methodAlgebra (MethodF method ns path) =
-- Method nodes are leaves - just wrap in list
[MethodInfo method path ns]
-- | Monadic algebra for collecting methods (for hyloM)
methodAlgebraM :: SchemaF [MethodInfo] -> SynapseM [MethodInfo]
methodAlgebraM = pure . methodAlgebra
-- ============================================================================
-- Coalgebras
-- ============================================================================
-- | Coalgebra for unfolding schema trees
--
-- This is a proper F-coalgebra: Path -> SynapseM (SchemaF Path)
-- It shows how to produce one layer from a seed.
schemaCoalgebra :: Path -> SynapseM (SchemaF Path)
schemaCoalgebra path = do
schema <- fetchSchemaAt path
let childPaths = case psChildren schema of
Nothing -> []
Just children -> [path ++ [csNamespace c] | c <- children]
pure $ PluginF schema path childPaths
-- ============================================================================
-- Walking Operations
-- ============================================================================
-- | Walk the schema tree, collecting all methods
--
-- This is a hylomorphism: unfold from path, fold with method algebra.
-- Uses hyloM for the fused monadic version - no intermediate tree built!
--
-- @
-- walkMethods = hyloM methodAlgebraM schemaCoalgebra
-- @
walkMethods :: Path -> SynapseM [MethodInfo]
walkMethods = hyloM methodAlgebraM schemaCoalgebra
-- | Walk schema tree with a custom monadic algebra
--
-- General version - provide your own algebra.
walkSchema :: (SchemaF a -> SynapseM a) -> Path -> SynapseM a
walkSchema alg = hyloM alg schemaCoalgebra
-- | Fold over methods with pure transformations
--
-- Convenience wrapper: walk to collect methods, then map and combine.
foldMethods :: (MethodInfo -> a) -- ^ Transform each method
-> ([a] -> b) -- ^ Combine results
-> Path -- ^ Starting path
-> SynapseM b
foldMethods f combine path = do
methods <- walkMethods path
pure $ combine $ map f methods