Skip to content

Commit 4d6b44c

Browse files
Add preserved method docs
1 parent 9021953 commit 4d6b44c

File tree

1 file changed

+51
-0
lines changed

1 file changed

+51
-0
lines changed

docs/reference/python-integration.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ We define a custom "primitive sort" (i.e. a builtin type) for `PyObject`s. This
1515
To create an expression of type `PyObject`, we have to use the `egraph.save_object` method. This method takes a Python object and returns an expression of type `PyObject`.
1616

1717
```{code-cell} python
18+
from __future__ import annotations
1819
from egglog import *
20+
1921
egraph = EGraph()
2022
one = egraph.save_object(1)
2123
one
@@ -90,3 +92,52 @@ assert egraph.load_object(egraph.extract(evalled)) == 3
9092
```
9193

9294
This is a bit subtle at the moment, and we plan on adding an easier wrapper to eval arbitrary Python code in the future.
95+
96+
## "Preserved" methods
97+
98+
You can use the the `@egraph.method(preserve=True)` decorator to mark a method as "preserved", meaning that calling it will actually execute the body of the function and a coresponding egglog function will not be created,
99+
100+
Normally, all methods defined on a egglog `Expr` will ignore their bodies and simply build an expression object based on the arguments.
101+
102+
However, there are times in Python when you need the return type of a method to be an instance of a particular Python type, and some similar acting expression won't cut it.
103+
104+
For example, let's say you are implementing a `Bool` expression, but you want to be able to use it in `if` statements in Python. That means it needs to define a `__bool__` methods which returns a Python `bool`, based on evaluating the expression.
105+
106+
```{code-cell} python
107+
@egraph.class_
108+
class Bool(Expr):
109+
@egraph.method(preserve=True)
110+
def __bool__(self) -> bool:
111+
# Add this expression converted to a Python object to the e-graph
112+
egraph.register(self)
113+
# Run until the e-graph saturates
114+
egraph.run(run().saturate())
115+
# Extract the Python object from the e-graph
116+
return egraph.load_object(egraph.extract(self.to_py()))
117+
118+
def to_py(self) -> PyObject:
119+
...
120+
121+
def __or__(self, other: Bool) -> Bool:
122+
...
123+
124+
TRUE = egraph.constant("TRUE", Bool)
125+
FALSE = egraph.constant("FALSE", Bool)
126+
127+
128+
@egraph.register
129+
def _bool(x: Bool):
130+
return [
131+
set_(TRUE.to_py()).to(egraph.save_object(True)),
132+
set_(FALSE.to_py()).to(egraph.save_object(False)),
133+
rewrite(TRUE | x).to(TRUE),
134+
rewrite(FALSE | x).to(x),
135+
]
136+
```
137+
138+
Now whenever the `__bool__` method is called, it will actually execute the body of the function, and return a Python `bool` based on the result.
139+
140+
```{code-cell} python
141+
if TRUE | FALSE:
142+
print("True!")
143+
```

0 commit comments

Comments
 (0)