packages feed

plexus-synapse-0.3.0.0: src/Synapse/Schema/Functor.hs

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE UndecidableInstances #-}

-- | Base functor for schema trees
--
-- This module defines the "shape" of one layer of a schema tree,
-- enabling proper recursion schemes (ana/cata/hylo).
--
-- = The Functor
--
-- @
-- data SchemaF a
--   = PluginF PluginSchema [a]   -- interior node: plugin with children
--   | MethodF MethodSchema Text  -- leaf: method with namespace
-- @
--
-- The type parameter 'a' represents "what's in the recursive positions".
-- For a tree, 'a' would be the subtrees. For a fold result, 'a' would be
-- the accumulated value from children.
--
-- = Fixed Point
--
-- The fixed point @Fix SchemaF@ gives us the infinite tree type:
--
-- @
-- type SchemaTree = Fix SchemaF
-- @
--
-- This is isomorphic to a tree where every node is either a plugin
-- (with children) or a method (leaf).
module Synapse.Schema.Functor
  ( -- * Base Functor
    SchemaF(..)

    -- * Fixed Point
  , Fix(..)
  , SchemaTree

    -- * Seed for unfolding
  , SchemaSeed(..)

    -- * Accessors
  , nodeNamespace
  , nodeMethods
  ) where

import Data.Text (Text)

import Synapse.Schema.Types (PluginSchema(..), MethodSchema(..), Path)

-- | Fixed point of a functor
--
-- @Fix f@ is the type where @f@ refers to itself in recursive positions.
-- Unrolling: @Fix f ≅ f (Fix f) ≅ f (f (Fix f)) ≅ ...@
newtype Fix f = Fix { unFix :: f (Fix f) }

deriving instance Show (f (Fix f)) => Show (Fix f)
deriving instance Eq (f (Fix f)) => Eq (Fix f)

-- | Base functor for schema trees
--
-- Captures the "shape" of one layer:
-- - PluginF: interior node with plugin info and child positions
-- - MethodF: leaf with method info and parent namespace
data SchemaF a
  = PluginF
      { sfSchema    :: PluginSchema  -- ^ The plugin schema at this node
      , sfPath      :: Path          -- ^ Path to this node
      , sfChildren  :: [a]           -- ^ Recursive positions (children)
      }
  | MethodF
      { sfMethod    :: MethodSchema  -- ^ The method schema
      , sfNamespace :: Text          -- ^ Parent namespace
      , sfMethodPath :: Path         -- ^ Full path to this method
      }
  deriving stock (Functor, Foldable, Traversable, Show, Eq)

-- | The fixed point gives us schema trees
type SchemaTree = Fix SchemaF

-- | Seed for anamorphic unfolding
-- Contains the path to fetch and any context needed
data SchemaSeed = SchemaSeed
  { seedPath :: Path
  }
  deriving stock (Show, Eq)

-- | Get the namespace from a schema node
nodeNamespace :: SchemaF a -> Text
nodeNamespace (PluginF schema _ _) = psNamespace schema
nodeNamespace (MethodF _ ns _) = ns

-- | Get methods from a plugin node (empty for method nodes)
nodeMethods :: SchemaF a -> [MethodSchema]
nodeMethods (PluginF schema _ _) = psMethods schema
nodeMethods (MethodF _ _ _) = []