Are templated string embedded expressions "method parameters" or "lambdas"?

Jim Laskey james.laskey at oracle.com
Fri Oct 29 14:10:54 UTC 2021


For our early templated string prototypes, we restricted embedded expressions to just basic accessors and basic arithmetic. The intent was to keep things easy to read and to prevent side effects. Over time, we began thinking this restriction was unduly harsh. More precisely, we worried it that it would result in a complex, difficult-to-defend boundary. But we still would like users to not rely on side-effects.

Consequently, a new proposal for embedded expressions - we would allow any Java expression with the restriction that you can't use single quotes, double quotes or escape sequences. We opted to keep this restriction to allow tools (ex., syntax highlighters) to isolate embedded expressions within strings without requiring sophisticated parsing.

Given that an unprocessed templated string involves at least some deferred evaluation, should we frame templated string parameters as being more like method parameters (all parameters evaluated eagerly, left to right), or should we treat them as lambda expressions, which may capture (effectively final) variables from the environment, and evaluate the full parameters expressions when they are needed?

Note too that the effectively final restriction rules out some of the worst side-effect offenders, like:

    int x = 0;
    formatter."One \{x++} plus two \{x++} is three \{x}";

-- even if we intend to then do eager evaluation!

To help understand the issue, let's look at a simplification of how the two different paradigms (method parameter vs. lambda) might be implemented. Example:

    int x = 0;

    int method1() {
        System.out.println("one");
        return 1;
    }

    int method2() {
        System.out.println("two");
        return 2;
    }

    System.out.println("Before TemplatedString");
    TemplatedString ts = "\{x} and \{method1()} and \{method2()}";
    System.out.println("After TemplatedString");
    System.out.println(CONCAT.apply(ts));
    System.out.println("After Policy");

The method parameter paradigm would generate something like following for TemplatedString ts = "\{x} and \{method1()} and \{method2()}"; statement. Basically, capture the values of the evaluated expressions in instance fields.

    TemplatedString ts = new TemplatedString() {
        int expr$0 = x;
        int expr$1 = method1();
        int expr$2 = method2();

        String template() {
            return "\uFFFC and \uFFFC and \uFFFC";
        }

        List<Object> values() {
            return List.of(expr$0, expr$1, expr$2);
        }

        String concat() {
            return expr$0 + " and " + expr$1 + " and " + expr$2;
        }

        List<MethodHandle> vars() {
            return List.of(lookupGetter("expr$0"), lookupGetter("expr$1"), lookupGetter("expr$2"));
        }
    }

The lambda paradigm would generate something like following. Basically, wrap the expression in an instance method and capturing effectively final values used by the methods in instance fields (ala lambda.)

    TemplatedString ts = new TemplatedString() {
int var$x = x;

        int expr$0() {
            return var$x;
        }

        int expr$1() {
            return method1();
        }

        int expr$2() {
            return method2();
        }

        String template() {
            return "\uFFFC and \uFFFC and \uFFFC";
        }

        List<Object> values() {
            return List.of(expr$0(), expr$1(), expr$2());
        }

        String concat() {
            return expr$0() + " and " + expr$1() + " and " + expr$2();
        }

        List<MethodHandle> vars() {
            return List.of(lookupMethod("expr$0"), lookupMethod("expr$1"), lookupMethod("expr$2"));
        }
    }

The output from the method parameter paradigm would be:

    Before TemplatedString
    one
    two
    After TemplatedString
    0 and 1 and 2
    After Policy

>From the lambda paradigm would be:

    Before TemplatedString
    After TemplatedString
    one
    two
    0 and 1 and 2
    After Policy

To help us evaluating the tradeoffs between the two paradigms, our question to the experts is, "What are the ramifications of each?" Please resist the temptation to express a preference for one or the other.

Thank you.

Cheers,

-- Jim
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20211029/0c483082/attachment-0001.htm>


More information about the amber-spec-experts mailing list