Hi, sorry I meant to respond to the list too. Messages included below. ---------- Forwarded message ---------- From: MG <mg...@arscreat.com> Date: Sat, Mar 10, 2018 at 7:19 AM Subject: Re: About supporting `var` of Java10+ To: pa...@asert.com.au
Hi Paul, did you reply to just me on purpose ? On 09.03.2018 03:11, Paul King wrote: I am mostly worried about non-denotable types. Perhaps we could see how far we can get just applying the same rules that Java will: http://openjdk.java.net/jeps/286#Non-denotable-types I feel like all the non-denotable types are pretty rare edge cases in practice, so maybe using var === def for them would be ok... ? Having said that, the non-denotable types they list are: # A null-typed variable is practically useless, and there is no good alternative for an inferred type, so we reject these. I agree => error if assigned value is null. # Allowing capture variables to flow into subsequent statements adds new expressiveness to the language, but that's not a goal of this feature. Instead, the proposed projection operation is one we need to use anyway to address various bugs in the type system (see, e.g., JDK-8016196), and it's reasonable to apply it here. I am not sure how this maps to Groovy... # Intersection types are especially difficult to map to a supertype—they're not ordered, so one element of the intersection is not inherently "better" than the others. The stable choice for a supertype is the lub of all the elements, but that will often be Object or something equally unhelpful. So we allow them. I have never used intersection types. I would really be interested to know how many people have ever used them. I tried the following in Groovy, but got a Error:(32, 17) Groovyc: [Static type checking] - No such property: s for class: T compile time error (even though IntelliJ did not complain about the construct): @CompileStaticclass GroovyIntersectionTypesSpike { static interface Intf0 { Number getX() } static interface Intf1 { String getS() } @Canonical static class Foo implements Intf0, Intf1 { Number x; String s } @Test void intersectionTypesTest() { print(new Foo(123,'abc')) } public <T extends Intf0 & Intf1> void print(final T t) { println "x=$t.x" println "s=$t.s" } } # Anonymous class types cannot be named, but they're easily understood—they're just classes. Allowing variables to have anonymous class types introduces a useful shorthand for declaring a singleton instance of a local class. We allow them. How do you assign a new value to a variable that has a type that exists only once: @CompileStaticclass GroovyAnonymousTypesSpike { @Canonical static class Foo { String s } @Test void anonymousTypesTest() { Foo x0a = new Foo("x0a") {} Foo x0b = new Foo("x0b") {} printFoos(x0a, x0b) Foo x1a = createAnonymousFooChild("x1a") Foo x1b = createAnonymousFooChild("x1b") printFoos(x1a, x1b) } Foo createAnonymousFooChild(String s ) { return new Foo(s) {} } void printFoos(Foo f0, Foo f1) { println "\nprintFoos:" printFoo(f0) printFoo(f1) println "are foos playing ball: ${f0.is(f1)}" } void printFoo(Foo f) { println "${f.getClass().name} (${f.getClass().canonicalName}): s=$f.s" } @Test void intersectionTypesTest2() { def afc0 = new Foo() { String speak() { return "Foo Child0: I am ${getClass().name} (${getClass().canonicalName})" } } def afc1 = new Foo() { String speak() { return "Foo Child1: I am ${getClass().name} (${getClass().canonicalName})" } } println afc0.speak() println afc1.speak() } } printFoos: groovy.GroovyAnonymousTypesSpike$1 (null): s=x0a groovy.GroovyAnonymousTypesSpike$2 (null): s=x0b are foos playing ball: false printFoos: groovy.GroovyAnonymousTypesSpike$3 (null): s=x1a groovy.GroovyAnonymousTypesSpike$3 (null): s=x1b are foos playing ball: false Maybe I am overlooking something, but using the anonymous type here looks like it would effectively make the variable final. So it looks like using the non-anonymous super class might make more sense here (it would not break Java compatibility, and would give programmers more options, if final is also changed in Groovy to use the RHS type instead of Object). So some of the simple immediate implications are: * error if var used for fields As I said supporting that would feel Groovy to me, so if it is easy to do I would support it, but I don't see it as essential in any way. * error if var used with no initializer statement Agreed. Or if null type is assigned. * we'll need some kind of marker in the AST for var (as Jochen already mentioned I think) Yes, Jochen did say that. The devil will be in the detail when we try to update the type checker I assume the replacement var x = RHS becoming typeof(RHS) x = RHS cannot be done before the type checker runs ? - and for dynamic Groovy I suspect we might need some additional restrictions in addition to those chosen by Java. Stupid question: Why ?-) Isn't the dynamic case just 1) var -> typeof(RHS) 2) done ? I hope we can make this happen... Markus