[Python-ideas] Symbolic expressions (or: partials and closures from the inside out)

Nathan Rice nathan.alexander.rice at gmail.com
Sun Jan 15 08:04:11 CET 2012


> The pattern you employ with SymbolicObject, is a nice way to mix code that is
> executed immediately, with code that is executed later. Symbolic algebra
> languages, like Maple and Mathematica, function this way too.

I find it to be useful quite frequently.  I think making generative
SQL expressions with SQL Alchemy got me hooked.

> I propose however a slightly different interface:
>
> There should be a special object "quoted" to create instances of
> "SymbolicObject". The objects should be created in the "__getattribute__"
> method. This way the objects know their name, which is quite useful. A
> symbolic object would be created like this:
>
>    X = quoted.X
>
> You could also write:
>
>    const = Constraints(quoted.X * 2 + 1 >= 5, quoted.X % 2 != 0)

The factory there seems to add complexity.  The Class(args)
constructor style is less magic, and I can imagine wanting to pass
things to the symbolic object, like a name, default value, a
docstring, etc.

> The standard library should contain at least these symbolic algorithms:
>
> evalsym(tree, environment, return_type="any")
>    Evaluates an expression and returns the result. Works similar to:
>
>    eval(compile(tree, "", "eval"), environment)
>
>    This call would return 7:
>
>        evalsym(quoted.X * 2 + 1, {"X":3})
>
>    However it should do partial evaluation if some symbols are
>    unknown, and return an ast.AST in this case (similar to Maple and
>    Mathematica). This expression should be True:
>
>        evalsym(quoted.X + quoted.Y, {"Y":3}) == quoted.X + 3
>
>    The optional argument "return_type" specifies the type of the
>    result. This simplifies algorithms using "eval". Argument
>    "return_type" can be: "any", "ast", or "object".

I think a method call leads to more readable code than a function,
because they grow left to right and are structured like English.
Something along the lines of:

expr = X ** (M*(N+1))
expr.realize({N:2, M:3, X:4})
expr.realize((N,2)).realize((M, 3)).realize((X, 4))

Tasty curry.

As a side note, perhaps PyPy is a good place to prototype something
like this? :)

Nathan



More information about the Python-ideas mailing list