Predicate and Symbolic Function

Predicate and Symbolic Function#

Custom predicates allow you to encapsulate reusable boolean logic that can be used inside queries. In addition, any Python function decorated with @symbolic_function can be used. A Predicate class is a dataclass that inherits from EQL Predicate class. These two become symbolic variables when any of the arguments are variables of a query. If all the arguments are ordinary python objects, you can use them normally.

Lets first define our model and some sample data.

from dataclasses import dataclass
from typing_extensions import List

from krrood.entity_query_language.entity import entity, let, an, Symbol
from krrood.entity_query_language.predicate import Predicate, symbolic_function


@dataclass
class Body(Symbol):
    name: str


@dataclass
class Handle(Body):
    pass


@dataclass
class Container(Body):
    pass


@dataclass
class World(Symbol):
    id_: int
    bodies: List[Body]
    
# Sample world containing containers and handles
world = World(
    1,
    [
        Container("Container1"),
        Container("Container2"),
        Handle("Handle1"),
        Handle("Handle2"),
        Handle("Handle3"),
    ],
)

Now lets define a custom predicate and symbolic function and use them in a query.

# Define a reusable symbolic function: returns True if a body is a handle by name convention
@symbolic_function
def is_handle(body_: Body) -> bool:
    return body_.name.startswith("Handle")


@dataclass
class HasThreeInItsName(Predicate):
    body: Body

    def __call__(self):
        return '3' in self.body.name

# Build the query using the predicate inside symbolic mode
query = an(
    entity(
        body := let(type_=Body, domain=world.bodies),
        is_handle(body_=body),  # use the predicate just like any other condition
        HasThreeInItsName(body)
    )
)

# Evaluate and inspect the results
results = list(query.evaluate())
assert len(results) == 1
assert isinstance(results[0], Handle)
assert results[0].name == "Handle3"
print(*results, sep="\n")
Handle(name='Handle3')