How the Expression Class Extends the Symbol Class in SymbolicAI

The Expression class inherits from Symbol to add lazy evaluation capabilities, deferring computation until the expression is called while maintaining full access to Symbol's value-handling and operator-overload functionality.

In the SymbolicAI framework, the relationship between Symbol and Expression forms the foundation of its neuro-symbolic computation model. While Symbol (defined in symai/symbol.py) serves as the base wrapper for values and enables dual-mode syntactic/semantic operations, Expression extends this foundation to introduce deferred execution patterns essential for building complex computational graphs and LLM-driven workflows.

Core Inheritance: Expression as a Symbol Subclass

Expression is a direct subclass of Symbol, inheriting all core value-container capabilities implemented in symai/symbol.py. This inheritance provides Expression with:

  • Value storage via self._value, allowing expressions to wrap and transport data
  • Basic operator overloads (+, ==, etc.) that delegate to primitive operations
  • Dual-mode behavior enabling the framework to operate either as standard Python or through LLM engines

By inheriting these capabilities, Expression instances can participate in arithmetic operations, comparisons, and data transformations exactly like standard Symbol objects, ensuring seamless integration within the SymbolicAI ecosystem.

Lazy Evaluation: The Key Extension

The primary extension Expression adds to Symbol is lazy evaluation — the ability to defer computation until the expression is explicitly invoked. This transforms static values into executable computational nodes.

The Abstract forward Method

Expression introduces an abstract forward(self, *args, **kwargs) method that subclasses must implement. This method defines the actual computation logic but remains unexecuted when the Expression object is instantiated. The signature appears in symai/symbol.py as:

class Expression(Symbol):
    def forward(self, *args, **kwargs):
        raise NotImplementedError("Subclasses must implement forward()")

Overriding call for Deferred Execution

Expression overrides __call__ to trigger the lazy evaluation mechanism. When an Expression instance is called like a function, the overridden __call__ method:

  1. Invokes the forward method with provided arguments
  2. Caches the computation result
  3. Returns a Symbol-like object containing the evaluated value

This mechanism allows Expression objects to behave as both unevaluated computational graphs (before calling) and concrete values (after calling), bridging the gap between symbolic representations and actual execution.

Integration with the Processing Pipeline

Beyond lazy evaluation, Expression extends Symbol by connecting to SymbolicAI's pre-processor and post-processor pipeline defined in symai/components.py. Through intermediate classes like TrackerTraceable and Function, Expression enables:

  • Prompt-driven LLM calls to be embedded as executable expressions
  • Automatic tracking of computation traces for debugging and optimization
  • Pipeline composition where expressions can be chained with pre-processing and post-processing steps

The class hierarchy extends as follows:


Symbol (symai/symbol.py)
 └── Expression (symai/symbol.py)
      └── TrackerTraceable (symai/components.py)
           └── Function (symai/components.py)

This architecture allows Function objects (which inherit from Expression via TrackerTraceable) to wrap LLM prompts as lazy-evaluated expressions that execute only when called, while maintaining all Symbol capabilities for value handling.

Practical Examples

Simple Lazy Expression

The following example demonstrates how Expression extends Symbol to implement custom lazy computation:

from symai import Symbol, Expression

class AddOne(Expression):
    def forward(self, x: Symbol) -> Symbol:
        # Computation deferred until expression is called

        return x + 1

# Create concrete Symbol values

a = Symbol(5)          # Immediate value: 5

expr = AddOne()        # Lazy expression: unevaluated

# Trigger evaluation

result = expr(a)       # Calls forward(5), returns Symbol(6)

print(result)          # Output: 6

In this example, AddOne inherits value storage from Symbol and adds lazy evaluation through Expression. The forward method defines the computation, but execution only occurs when expr(a) is called.

LLM-Driven Expression

For AI-powered operations, Function (which extends Expression through TrackerTraceable) enables prompt-driven lazy evaluation:

from symai import Function

class SummarizeText(Function):
    # Function automatically handles Expression mechanics

    prompt = "Summarize the following text in one sentence:\n{{text}}"

# Instantiate lazy LLM expression

summary = SummarizeText()

# Prepare input as Symbol

text = Symbol("Artificial intelligence enables machines to reason like humans.")

# Execute LLM call and return result as Symbol

result = summary(text)  # Triggers forward, calls LLM, returns Symbol

print(result)         # Symbol containing the summary text

Here, SummarizeText leverages the Expression extension mechanism to defer the LLM API call until the expression is invoked, while the underlying Symbol infrastructure handles value wrapping and operator support.

Summary

  • Inheritance foundation: Expression directly subclasses Symbol in symai/symbol.py, inheriting value storage (self._value) and operator overload capabilities.

  • Lazy evaluation mechanism: Expression introduces the abstract forward method and overrides __call__ to defer computation until invocation, caching results as Symbol objects.

  • Pipeline integration: Through intermediate classes in symai/components.py, Expression connects to pre-processors and post-processors, enabling LLM-driven Function objects to operate as lazy-evaluated symbols.

  • Dual-mode compatibility: By extending Symbol, Expression maintains the framework's ability to operate both as standard Python code and as semantic LLM-powered operations.

Frequently Asked Questions

What is the difference between Symbol and Expression in SymbolicAI?

Symbol is the base value-wrapper class that provides data storage and operator overloads, while Expression is a specialized subclass that adds lazy evaluation capabilities. Symbol represents immediate values, whereas Expression represents deferred computations that only execute when called. Both classes reside in symai/symbol.py, with Expression inheriting all Symbol functionality while extending it with the forward method and __call__ override.

How does lazy evaluation work in the Expression class?

Lazy evaluation in Expression works through the abstract forward method and the overridden __call__ method. When you create an Expression subclass, you implement forward to define the computation logic, but this code does not run immediately. Instead, when the expression instance is called (e.g., expr(args)), the __call__ method triggers forward, caches the result, and returns it as a Symbol object. This pattern allows complex computational graphs to be constructed and optimized before execution.

Can I use Expression without implementing the forward method?

No, you cannot use Expression directly without implementing the forward method. The base Expression class defines forward as an abstract method that raises NotImplementedError. Attempting to instantiate Expression directly or call an instance of a subclass that hasn't implemented forward will result in an error. Every concrete Expression subclass must override forward to specify the actual computation, whether that's simple arithmetic, data transformation, or LLM-powered operations via the Function class in symai/components.py.

How does Expression connect to LLM processing in SymbolicAI?

Expression connects to LLM processing through the class hierarchy that extends into symai/components.py. The Function class inherits from TrackerTraceable, which in turn inherits from Expression. This inheritance chain allows Function objects to utilize the lazy evaluation mechanism of Expression while adding pre-processor and post-processor pipelines. When a Function instance is called, it uses the inherited __call__ behavior to trigger its forward method, which formats prompts and invokes the configured LLM engine, returning the result as a Symbol object.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →