# How to Create Custom Neuro-Symbolic Operations Using the Expression Class in SymbolicAI

> Learn to create custom neuro-symbolic operations in SymbolicAI by subclassing the Expression class. Implement forward logic and control result types for powerful AI.

- Repository: [ExtensityAI/symbolicai](https://github.com/extensityai/symbolicai)
- Tags: how-to-guide
- Published: 2026-03-01

---

**To create custom neuro-symbolic operations in SymbolicAI, subclass the `Expression` class from `symai.symbol`, implement the abstract `forward` method with your operation logic, and optionally set `_sym_return_type` to control result casting.**

Creating custom neuro-symbolic operations allows you to extend SymbolicAI's lazy evaluation engine with domain-specific logic. The `Expression` class serves as the primary abstraction for defining operations that integrate seamlessly into the symbolic graph, supporting both pure Python implementations and future LLM-driven replacements. This guide demonstrates how to leverage the `Expression` architecture to build reusable, composable neuro-symbolic primitives.

## Understanding the Expression Class Architecture

The `Expression` class in [`symai/symbol.py`](https://github.com/extensityai/symbolicai/blob/main/symai/symbol.py) (lines 1039-1134) extends `Symbol` to provide lazy evaluation capabilities. When you subclass `Expression`, you inherit the full graph infrastructure, metadata handling, and primitive mixing machinery.

### Core Mechanisms

**Inheritance from Symbol** – `Expression` derives from `Symbol` at line 1039, giving your custom operations access to the symbolic graph, automatic metadata tracking, and primitive mix-ins.

**Lazy Evaluation via `__call__`** – The `__call__` method (lines 1054-1068) triggers evaluation only when the expression is invoked. It executes `forward`, then automatically links the result to the symbolic graph using `_root_link`.

**Result Typing with `_sym_return_type`** – This attribute (initialized around line 5050) defines the type that the expression casts to after evaluation. By default, it returns `type(self)`, but you can override it to return specific `Symbol` subclasses.

**Abstract `forward` Method** – Subclasses must implement `forward(self, *args, **kwargs) -> Symbol` (lines 1120-1134). This method contains the actual operation logic, whether pure Python, external API calls, or LLM prompts.

## Implementing a Custom Expression

Creating a working neuro-symbolic operation requires three steps: subclassing, implementing the forward logic, and configuring the return behavior.

### Step 1: Subclass Expression

Import `Expression` from `symai.symbol` and create your subclass. The inheritance automatically registers your class within the symbolic framework.

```python
from symai import Expression, Symbol

class MyOperation(Expression):
    """Custom neuro-symbolic operation."""
    def __init__(self):
        super().__init__()
        # Optional: define return type casting

        self._sym_return_type = Symbol

```

### Step 2: Override the forward Method

Implement the `forward` method to define the operation's behavior. This method receives `Symbol` instances as arguments and must return a `Symbol`.

```python
    def forward(self, a: Symbol, b: Symbol) -> Symbol:
        # Access raw values via .value property

        result = a.value + b.value
        # Return as Symbol to maintain graph compatibility

        return Symbol(result)

```

### Step 3: Configure Return Type

Set `_sym_return_type` in `__init__` to automatically cast results to specific types. This enables type-specific method chaining after evaluation.

```python
from symai import Result  # hypothetical Result subclass

class TypedOperation(Expression):
    def __init__(self):
        super().__init__()
        self._sym_return_type = Result  # Results auto-cast to Result type

```

## Practical Examples

### Example 1: Simple Arithmetic Operation

This example demonstrates a basic `Expression` that adds two symbols. The implementation resides in pure Python but follows the neuro-symbolic pattern.

```python
from symai import Symbol, Expression

class Add(Expression):
    """Lazy addition of two symbols."""
    def forward(self, a: Symbol, b: Symbol) -> Symbol:
        # Unwrap Symbol values and return new Symbol

        return Symbol(a.value + b.value)

# Usage

x = Symbol(3)
y = Symbol(7)
expr = Add()(x, y)       # __call__ triggers forward and links to graph

print(expr.value)        # → 10

print(expr.linker)       # displays graph connection

```

*Key implementation details:* The `Add` class inherits from `Expression` at line 1039 of [`symbol.py`](https://github.com/extensityai/symbolicai/blob/main/symbol.py). When instantiated and called, the `__call__` method (lines 1054-1068) executes `forward` and automatically invokes `_root_link` to attach the result to the symbolic graph.

### Example 2: LLM-Powered Summarization

This example uses the `@core.prompt` decorator to create an `Expression` backed by an LLM. The decorator transforms the method into a lazy evaluation that queries the language model.

```python
from symai import Expression, Symbol, core

class Summarize(Expression):
    """Neuro-symbolic summarization using LLM."""
    @core.prompt(message="Summarize the following text:", temperature=0.2)
    def forward(self, text: Symbol) -> Symbol:
        # The decorator intercepts this method; body is placeholder

        pass

# Usage

document = Symbol("Long article content...")
summary = Summarize()(document)    # Triggers LLM call via decorator

print(summary.value)             # LLM-generated summary

```

*Implementation context:* The `@core.prompt` decorator is defined in [`symai/core.py`](https://github.com/extensityai/symbolicai/blob/main/symai/core.py) and integrates with the `Expression` architecture by wrapping the `forward` method. The `Expression` base class handles the lazy evaluation and graph linking (lines 1054-1068 in [`symbol.py`](https://github.com/extensityai/symbolicai/blob/main/symbol.py)), while the decorator manages the LLM interaction.

### Example 3: Functional Composition with ExpressionBuilder

For complex pipelines, `ExpressionBuilder` in [`symai/components.py`](https://github.com/extensityai/symbolicai/blob/main/symai/components.py) provides a functional interface to chain multiple `Expression` steps without explicit subclassing.

```python
from symai.components import ExpressionBuilder

# Build a computation pipeline: read → double → add constant

pipeline = ExpressionBuilder() \
    .add(lambda _: 4) \
    .add(lambda x: x * 2) \
    .add(lambda x: x + 10)

result = pipeline()        # Evaluates the chain

print(result.value)        # → 18

```

*Architecture note:* `ExpressionBuilder` internally creates temporary `Expression` objects for each step, leveraging the same lazy evaluation mechanism defined in [`symai/symbol.py`](https://github.com/extensityai/symbolicai/blob/main/symai/symbol.py). This approach is useful for rapid prototyping before converting stable logic into formal `Expression` subclasses.

## Advanced Features

### Runtime Primitives

You can extend symbols with custom operations at runtime using the `primitive` method. This adds bound methods to individual `Symbol` instances without modifying global classes.

```python
from symai import Symbol

def square(self):
    return self.value ** 2

s = Symbol(5)
s.primitive("square", square)   # Defined in symbol.py lines 642-681

print(s.square())               # → 25

```

### Graph Linking and Symbolic Graph

Every `Expression` evaluation automatically links results into the symbolic graph via `_root_link` (lines 996-1024 in [`symbol.py`](https://github.com/extensityai/symbolicai/blob/main/symbol.py)). This enables:

- **Retrieval**: Later symbols can access previous computation results through the `linker` attribute.
- **Provenance**: Full traceability of how results were derived.
- **Composition**: Chaining expressions where each step references the graph state.

The `_sym_return_type` mechanism ensures that linked results maintain type safety, casting outputs to appropriate `Symbol` subclasses as defined in your `Expression` implementation.

## Summary

- **Subclass `Expression`** from `symai.symbol` to create lazy neuro-symbolic operations that integrate with the SymbolicAI graph engine.
- **Implement `forward`** to define the operation logic, accepting and returning `Symbol` instances; this method is invoked automatically when the expression is called.
- **Set `_sym_return_type`** in `__init__` to control automatic type casting of results, enabling method chaining with specific symbol types.
- **Leverage decorators** like `@core.prompt` from [`symai/core.py`](https://github.com/extensityai/symbolicai/blob/main/symai/core.py) to replace Python logic with LLM-driven implementations without changing the expression interface.
- **Use `ExpressionBuilder`** from [`symai/components.py`](https://github.com/extensityai/symbolicai/blob/main/symai/components.py) for functional composition of expression chains without explicit subclassing.
- **Access the graph** via the `linker` attribute and `_root_link` mechanism to maintain provenance and enable result retrieval across operations.

## Frequently Asked Questions

### What is the difference between Symbol and Expression in SymbolicAI?

`Symbol` is the base abstraction for all symbolic objects in SymbolicAI, providing the graph infrastructure, metadata handling, and primitive mix-ins. `Expression` is a specialized subclass of `Symbol` defined in [`symai/symbol.py`](https://github.com/extensityai/symbolicai/blob/main/symai/symbol.py) that adds lazy evaluation capabilities through the `__call__` method and requires subclasses to implement the `forward` method. While all `Expression` objects are `Symbol` instances, not all `Symbol` objects are lazy-evaluated expressions.

### How do I convert a regular Python function into an Expression?

To convert a Python function into a neuro-symbolic `Expression`, create a subclass of `Expression` and move your function logic into the `forward` method. Wrap the arguments and return values as `Symbol` instances to maintain graph compatibility. For example, if you have a function `def add(a, b): return a + b`, convert it by subclassing `Expression` and implementing `forward` to accept `Symbol` instances, extract their `.value` attributes, perform the addition, and return a new `Symbol` with the result.

### Can I use decorators to create Expressions?

Yes, SymbolicAI provides decorators in [`symai/core.py`](https://github.com/extensityai/symbolicai/blob/main/symai/core.py) that convert functions into `Expression`-backed operations. The `@core.prompt` decorator transforms a method into an LLM-driven expression, where the decorator parameters configure the language model call. When applied to a method within an `Expression` subclass, the decorator intercepts the `forward` invocation and replaces the Python logic with an LLM query, while still maintaining the lazy evaluation and graph linking provided by the `Expression` base class.

### Where are the core Expression classes defined?

The core `Expression` class is defined in [`symai/symbol.py`](https://github.com/extensityai/symbolicai/blob/main/symai/symbol.py) starting at line 1039, where it inherits from `Symbol`. The lazy evaluation logic resides in the `__call__` method (lines 1054-1068), while the abstract `forward` method is defined at lines 1120-1134. Helper utilities for building expression pipelines are located in [`symai/components.py`](https://github.com/extensityai/symbolicai/blob/main/symai/components.py), including the `ExpressionBuilder` class. Decorators for LLM integration are found in [`symai/core.py`](https://github.com/extensityai/symbolicai/blob/main/symai/core.py), and primitive operations are defined in [`symai/ops/primitives.py`](https://github.com/extensityai/symbolicai/blob/main/symai/ops/primitives.py).