Enhancing expressions with mutability?
Paul Sandoz
paul.sandoz at oracle.com
Mon Mar 23 16:00:30 UTC 2020
Hi,
Yes, interesting stuff.
I agree with Maurizio here, naturally because we have talked about this area a few times.
C# expression trees and LINQ is a great example. I think we can apply this more broadly to source compile time, link time and runtime, and more broadly to the source that can be “quoted” (or target typed, rather than built programmatically).
Such an IR could be an interchange format between various domains. A significant challenge is to find the right structural form to support many use cases. The machine learning area is particularly interesting to me in this respect. There appears to be an explosion in bespoke static compiler pipelines and analysis of code.
Paul.
> On Mar 23, 2020, at 7:31 AM, Maurizio Cimadamore <maurizio.cimadamore at oracle.com> wrote:
>
> Hey Bernard,
> cool stuff. Your patch seems to be missing the Term class, which is kind of important to look at to see how it interoperates with javac.
>
> That said, while the ideas you explore are interesting, I do not seem them being applicable 'as is' to Java. That said, one direction which overlaps a lot to what you describe here, especially in your derivative example is support for the so called expression trees feature - C# for example has it [1].
>
> Expression trees gives the language a new super power, in that it allows the static compiler to create a richer intermediate form for the compiled expression (e.g. other than bytecode), which is then amenable to further processing at runtime - e.g. we could take this IR and compile it down to:
>
> * regular bytecode - e.g. to generate a method handle
> * assembly - very useful for native interop (a la Panama)
> * GPU kernel - very useful for offloading computation to GPU or other resources
>
> At the same time, if the IR is expressive enough, you can do things like differentiating functions and the likes, which are use cases very important in machine learning.
>
> So, my feeling is that, at some point, we'll start seeing some more concrete experiments in that direction - stay tuned :-)
>
> Maurizio
>
> [1] - https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/expression-trees/
>
> On 10/03/2020 21:03, B. Blaser wrote:
>> Hi,
>>
>> Taking back the essential idea of the original publication on Lisp
>> [1], I did the attached syntactic experiment [2] trying to bring some
>> kind of mutability to expressions and thus giving more flexibility to
>> the language. I hope this subject hasn't been covered too many times,
>> otherwise I'd apologize for any disturbance.
>>
>> To build such mutable expressions, I've specified a new unary operator
>> ` along with its resulting class 'java.lang.Term'. The quotation
>> operator creates a 'Term' from any reference which can then be linked
>> to other terms to finally be evaluated to the result of a function
>> call for lambdas or the reference itself otherwise.
>>
>> Let's take some examples from the attached patch:
>>
>> + interface Get {
>> + int op();
>> + }
>> +
>> + interface Sum {
>> + int op(int i, int j);
>> + }
>>
>> + int a = 0, b = 0;
>> + Sum s = (x, y) -> x + y;
>>
>> + public void run() {
>> + Get a = () -> this.a;
>> + Get b = () -> this.b;
>> +
>> + Term e1 = (`s).link(`a, `b);
>> + this.a = 1; this.b = 2;
>>
>> The above lines create a mutable expression e1 from the sum lambda s
>> of the getter a and b which can then be evaluated to:
>>
>> (Integer)e1.value() == 3
>>
>> or visualized as:
>>
>> e1.toString().equals("Sum( Get( ) Get( ) )")
>>
>> An interesting application similar to §3.g from [1] is the computation
>> of the partial derivative of a sum involving the mutation of the
>> initial expression to produce a new resulting one:
>>
>> + // Derivative of sum e with respect to r
>> + Term derivative(Term e, Term r) {
>> + if (e.terms.isEmpty()) {
>> + return `Integer.valueOf(e.that == r.that ? 1 : 0);
>> + }
>> + else if(e.that == (`s).that) {
>> + return (`s).link(derivative(e.terms.get(0), r),
>> derivative(e.terms.get(1), r));
>> + }
>> + else throw new AssertionError();
>> + }
>>
>> Invoking the above method like this:
>>
>> + Term e2 = derivative(e1, `a);
>>
>> creates the resulting derivative expression e2 which can then be evaluated to:
>>
>> (Integer)e2.value() == 1
>>
>> or visualized as:
>>
>> e2.toString().equals("Sum( `1 `0 )")
>>
>> Finally, we note that any term may be quoted to prevent its
>> evaluation, for example:
>>
>> + Derivative d = this::derivative;
>>
>> + Term e3 = (`d).link(`e1, ``a);
>>
>> can be evaluated to:
>>
>> (Integer)((Term)e3.value()).value() == 1
>>
>> or visualized as:
>>
>> e3.toString().equals("Derivative( `Sum( Get( ) Get( ) ) `Get( ) )")
>>
>> Of course, one of our main concern is how much do we need this and a
>> reasonable answer would probably be as much as we need Lisp's
>> S-expressions but with similar or maybe better performance than
>> external interpreters and a more homogeneous integration to the
>> language.
>>
>> Any feedback about this experiment would be welcome!
>>
>> Thanks,
>> Bernard
>>
>> [1] http://www-formal.stanford.edu/jmc/recursive.html
>> [2] quote.patch
More information about the discuss
mailing list