From ali.ebrahimi1781 at gmail.com Fri Nov 5 21:55:28 2010 From: ali.ebrahimi1781 at gmail.com (Ali Ebrahimi) Date: Sat, 6 Nov 2010 08:25:28 +0330 Subject: Problem of defender methods with generic methods Message-ID: Hi Maurizio, when compiling the following code I get two error. public interface CustomizedList extends List { extension CustomizedList filter(Predicate predicate) default Trait.filter; extension CustomizedList map(Extrator predicate) default Trait.map; <========== cannot find symbol R > extension A max() default Trait.max; <============= cannot find symbol A public static class Trait { public static CustomizedList filter( final CustomizedList self, final Predicate predicate) { CustomizedList result=null; try { result = self.getClass().newInstance(); } catch (Throwable e) { e.printStackTrace(); } for(T elm:self){ if(predicate.op(elm)) result.add(elm); } return result; } public static CustomizedList map( final CustomizedList self, final Extrator extrator) { CustomizedList result=null; try { result = self.getClass().newInstance(); } catch (Throwable e) { e.printStackTrace(); } for(T elm:self){ result.add(extrator.extract(elm)); } return result; } public static > R max( final CustomizedList self) { if(self.size()==0) return null; R result=self.get(0); for(R elm:self){ if(elm.compareTo(result)>0) result=elm; } return result; } } } Best Regards, Ali Ebrahimi From maurizio.cimadamore at oracle.com Mon Nov 8 04:41:24 2010 From: maurizio.cimadamore at oracle.com (maurizio.cimadamore at oracle.com) Date: Mon, 08 Nov 2010 12:41:24 +0000 Subject: hg: lambda/lambda/langtools: Bug fixes: Message-ID: <20101108124130.46B31477DA@hg.openjdk.java.net> Changeset: b3cc870e2c67 Author: mcimadamore Date: 2010-11-08 12:40 +0000 URL: http://hg.openjdk.java.net/lambda/lambda/langtools/rev/b3cc870e2c67 Bug fixes: *) Regression: type-variables not visible when default implementation of generic extension method is lazily attributed *) Misc bugs causing compiler to generate classfiles in wrong location (caused some spurious regression failures when tests were run twice w/o cleanup) ! src/share/classes/com/sun/tools/javac/comp/Attr.java ! src/share/classes/com/sun/tools/javac/comp/MemberEnter.java ! src/share/classes/com/sun/tools/javac/comp/Unlambda.java + test/tools/javac/defender/Pos10.java From maurizio.cimadamore at oracle.com Mon Nov 8 04:42:23 2010 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 08 Nov 2010 12:42:23 +0000 Subject: Problem of defender methods with generic methods In-Reply-To: References: Message-ID: <4CD7F02F.2020504@oracle.com> Hi Ali this should be fixed now - it was a regression caused by recent work on extension methods. Thanks for the report. Maurizio On 06/11/10 04:55, Ali Ebrahimi wrote: > Hi Maurizio, > > when compiling the following code I get two error. > > public interface CustomizedList extends List { > extension CustomizedList filter(Predicate predicate) default > Trait.filter; > extension CustomizedList map(Extrator predicate) > default Trait.map;<========== cannot find symbol R > > extension A max() default > Trait.max;<============= cannot find symbol A > > public static class Trait { > > public static CustomizedList filter( > final CustomizedList self, final Predicate predicate) { > CustomizedList result=null; > try { > result = self.getClass().newInstance(); > } catch (Throwable e) { > e.printStackTrace(); > } > for(T elm:self){ > if(predicate.op(elm)) > result.add(elm); > } > return result; > } > > public static CustomizedList map( > final CustomizedList self, final Extrator extrator) { > CustomizedList result=null; > try { > result = self.getClass().newInstance(); > } catch (Throwable e) { > e.printStackTrace(); > } > for(T elm:self){ > result.add(extrator.extract(elm)); > } > return result; > } > > public static> R max( > final CustomizedList self) { > if(self.size()==0) > return null; > > R result=self.get(0); > for(R elm:self){ > if(elm.compareTo(result)>0) > result=elm; > } > return result; > } > } > } > > > Best Regards, > > Ali Ebrahimi > > From markmahieu at gmail.com Tue Nov 9 19:11:59 2010 From: markmahieu at gmail.com (Mark Mahieu) Date: Wed, 10 Nov 2010 03:11:59 +0000 Subject: Lambda as constructor argument (javac bug?) Message-ID: Hi, Should lambdas be allowed in the context of a constructor invocation? The latest SotL discusses lambda conversion w.r.t. method invocation, which I'd assumed applies also to constructors, but javac seems to be stricter about this: class UnexpectedLambda { public static void main(String[] args) { expect(#{}); // ok new UnexpectedLambda(#{}); // rejected } static void expect(Runnable r) {} UnexpectedLambda(Runnable r) {} } $ javac UnexpectedLambda.java UnexpectedLambda.java:5: lambda expression not expected here new UnexpectedLambda(#{}); // rejected ^ 1 error Regards, Mark From markmahieu at gmail.com Tue Nov 9 19:21:54 2010 From: markmahieu at gmail.com (Mark Mahieu) Date: Wed, 10 Nov 2010 03:21:54 +0000 Subject: javac bug: java.lang.AssertionError: unexpected type: Message-ID: <66005132-AADF-4A5A-B3A4-FCFB760BA097@gmail.com> Hi, Couldn't see any mention of this yet - compiling the following class triggers an AssertionError in javac. I'm using the latest code from hg (lambda/lambda/langtools). class Bang { interface Foo { T foo(T a, T b); } Foo f = #{ a, b -> a }; } java.lang.AssertionError: unexpected type: at com.sun.tools.javac.tree.TreeMaker.Type(TreeMaker.java:711) at com.sun.tools.javac.comp.Attr$2.inst(Attr.java:2225) at com.sun.tools.javac.comp.Infer.instantiateExpr(Infer.java:333) at com.sun.tools.javac.comp.Check.instantiatePoly(Check.java:453) at com.sun.tools.javac.comp.Check.checkType(Check.java:412) at com.sun.tools.javac.comp.Check.checkType(Check.java:405) at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2142) at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1497) at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:457) at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:475) at com.sun.tools.javac.comp.Attr.attribExpr(Attr.java:479) at com.sun.tools.javac.comp.Attr.visitVarDef(Attr.java:877) at com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:745) at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:457) at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:475) at com.sun.tools.javac.comp.Attr.attribStat(Attr.java:510) at com.sun.tools.javac.comp.Attr.attribClassBody(Attr.java:3719) at com.sun.tools.javac.comp.Attr.attribClass(Attr.java:3642) at com.sun.tools.javac.comp.Attr.attribClass(Attr.java:3578) at com.sun.tools.javac.main.JavaCompiler.attribute(JavaCompiler.java:1153) at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:843) at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:802) at com.sun.tools.javac.main.Main.compile(Main.java:418) at com.sun.tools.javac.main.Main.compile(Main.java:336) at com.sun.tools.javac.main.Main.compile(Main.java:327) at com.sun.tools.javac.Main.compile(Main.java:82) at com.sun.tools.javac.Main.main(Main.java:67) Cheers, Mark From isidore at setgame.com Wed Nov 10 05:16:27 2010 From: isidore at setgame.com (Llewellyn Falco) Date: Wed, 10 Nov 2010 05:16:27 -0800 Subject: Lambdas and serialization In-Reply-To: <4CC80634.5000908@oracle.com> References: <4CBDA6E7.1050501@oracle.com> <4CBDBCC1.9020706@univ-mlv.fr> <4CBDBC13.8060601@oracle.com> <4CBDE118.4030103@univ-mlv.fr> <4CBF15BA.4020703@univ-mlv.fr> <4CBF36B3.8020203@univ-mlv.fr> <4CC80634.5000908@oracle.com> Message-ID: I just realized this is a mistake! Comparable will not work properly without the Equals method being overridden. Namely, if you have two object a, b you will get a.compareTo(b) == 0 but a.equals(b) == false depending on your implementation this will fail! Llewellyn Falco http:\\bit.ly\lambdas On Wed, Oct 27, 2010 at 4:00 AM, Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > > btw: what happens with comparator? it's not a SAM (2 methods - compare, >> equals), but seems to be a very wanted case for lambdas? >> >> >> > The definition of SAM does not take into account methods inherithed from > java.lang.Object. Which means that Comparator really has just one > relevant target method and therefore can be used as the target of a SAM > conversion. > > Maurizio > From alessiostalla at gmail.com Wed Nov 10 05:40:38 2010 From: alessiostalla at gmail.com (Alessio Stalla) Date: Wed, 10 Nov 2010 14:40:38 +0100 Subject: Lambdas and serialization In-Reply-To: References: <4CBDA6E7.1050501@oracle.com> <4CBDBCC1.9020706@univ-mlv.fr> <4CBDBC13.8060601@oracle.com> <4CBDE118.4030103@univ-mlv.fr> <4CBF15BA.4020703@univ-mlv.fr> <4CBF36B3.8020203@univ-mlv.fr> <4CC80634.5000908@oracle.com> Message-ID: On Wed, Nov 10, 2010 at 2:16 PM, Llewellyn Falco wrote: > I just realized this is a mistake! > > Comparable will not work properly without the Equals method > being overridden. > > Namely, if you have two object a, b you will get > a.compareTo(b) == 0 > but > a.equals(b) == false > > depending on your implementation this will fail! Yes, but what's the point of implementing Comparable with a lambda? Comparable models an extra capability of a class of objects with other behaviour (say, Strings). In case of lambdas, the only behaviour is the implementation of the target SAM, so if you implement a Comparable with a lambda, you'll get an object with no capabilities except to compare itself with other objects - not very useful. Cheers, Alessio From mthornton at optrak.co.uk Wed Nov 10 06:03:29 2010 From: mthornton at optrak.co.uk (Mark Thornton) Date: Wed, 10 Nov 2010 14:03:29 +0000 Subject: Lambdas and serialization In-Reply-To: References: <4CBDA6E7.1050501@oracle.com> <4CBDBCC1.9020706@univ-mlv.fr> <4CBDBC13.8060601@oracle.com> <4CBDE118.4030103@univ-mlv.fr> <4CBF15BA.4020703@univ-mlv.fr> <4CBF36B3.8020203@univ-mlv.fr> <4CC80634.5000908@oracle.com> Message-ID: <4CDAA631.5040508@optrak.co.uk> On 10/11/2010 13:16, Llewellyn Falco wrote: > I just realized this is a mistake! > > Comparable will not work properly without the Equals method > being overridden. > > Namely, if you have two object a, b you will get > a.compareTo(b) == 0 > but > a.equals(b) == false From the JavaDoc for Comparable: "It is strongly recommended (though not required) that natural orderings be consistent with equals." Note it is a recommendation not a requirement. BigDecimal is an example where the natural ordering is not consistent with equals. Mark Thornton From alex.blewitt at gmail.com Wed Nov 10 06:29:11 2010 From: alex.blewitt at gmail.com (Alex Blewitt) Date: Wed, 10 Nov 2010 14:29:11 +0000 Subject: Lambdas and serialization In-Reply-To: References: <4CBDA6E7.1050501@oracle.com> <4CBDBCC1.9020706@univ-mlv.fr> <4CBDBC13.8060601@oracle.com> <4CBDE118.4030103@univ-mlv.fr> <4CBF15BA.4020703@univ-mlv.fr> <4CBF36B3.8020203@univ-mlv.fr> <4CC80634.5000908@oracle.com> Message-ID: That can't be right. Consider a PersonComparator which just compares first names. There may be two Alexs, but that doesn't mean they are the same Person. Sent from my iPhone 4 On 10 Nov 2010, at 13:16, Llewellyn Falco wrote: > I just realized this is a mistake! > > Comparable will not work properly without the Equals method > being overridden. > > Namely, if you have two object a, b you will get > a.compareTo(b) == 0 > but > a.equals(b) == false > > depending on your implementation this will fail! > > Llewellyn Falco > http:\\bit.ly\lambdas > > On Wed, Oct 27, 2010 at 4:00 AM, Maurizio Cimadamore < > maurizio.cimadamore at oracle.com> wrote: > >> >> btw: what happens with comparator? it's not a SAM (2 methods - compare, >>> equals), but seems to be a very wanted case for lambdas? >>> >>> >>> >> The definition of SAM does not take into account methods inherithed from >> java.lang.Object. Which means that Comparator really has just one >> relevant target method and therefore can be used as the target of a SAM >> conversion. >> >> Maurizio >> > From gdennis at alum.mit.edu Wed Nov 10 12:21:48 2010 From: gdennis at alum.mit.edu (Greg Dennis) Date: Wed, 10 Nov 2010 15:21:48 -0500 Subject: Lambdas and serialization Message-ID: A PersonComparator need not be consistent with the Person.equals() method, but if Person implements Comparable, it's compareTo method should be consistent with equals. Greg > Date: Wed, 10 Nov 2010 14:29:11 +0000 > From: Alex Blewitt > Subject: Re: Lambdas and serialization > To: Llewellyn Falco > Cc: "lambda-dev at openjdk.java.net" > Message-ID: > Content-Type: text/plain; ? ? ? charset=us-ascii > > That can't be right. Consider a PersonComparator which just compares first names. There may be two Alexs, but that doesn't mean they are the same Person. > > Sent from my iPhone 4 > > On 10 Nov 2010, at 13:16, Llewellyn Falco wrote: > >> I just realized this is a mistake! >> >> Comparable will not work properly without the Equals method >> being overridden. >> >> Namely, if you have two object a, b you will get >> a.compareTo(b) == 0 >> but >> a.equals(b) == false >> >> depending on your implementation this will fail! >> >> Llewellyn Falco >> http:\\bit.ly\lambdas >> >> On Wed, Oct 27, 2010 at 4:00 AM, Maurizio Cimadamore < >> maurizio.cimadamore at oracle.com> wrote: >> >>> >>> btw: what happens with comparator? it's not a SAM (2 methods - compare, >>>> equals), but seems to be a very wanted case for lambdas? >>>> >>>> >>>> >>> The definition of SAM does not take into account methods inherithed from >>> java.lang.Object. Which means that Comparator really has just one >>> relevant target method and therefore can be used as the target of a SAM >>> conversion. >>> >>> Maurizio From maurizio.cimadamore at oracle.com Wed Nov 10 12:43:48 2010 From: maurizio.cimadamore at oracle.com (maurizio cimadamore) Date: Wed, 10 Nov 2010 20:43:48 +0000 Subject: javac bug: java.lang.AssertionError: unexpected type: In-Reply-To: <66005132-AADF-4A5A-B3A4-FCFB760BA097@gmail.com> References: <66005132-AADF-4A5A-B3A4-FCFB760BA097@gmail.com> Message-ID: <4CDB0404.7020300@oracle.com> On 10/11/2010 03:21, Mark Mahieu wrote: > Hi, > > Couldn't see any mention of this yet - compiling the following class triggers an AssertionError in javac. I'm using the latest code from hg (lambda/lambda/langtools). Hi Mark Thanks for the report. I'm working on a fix. Maurizio > class Bang { > > interface Foo { > T foo(T a, T b); > } > > Foo f = #{ a, b -> a }; > } > > > java.lang.AssertionError: unexpected type: > at com.sun.tools.javac.tree.TreeMaker.Type(TreeMaker.java:711) > at com.sun.tools.javac.comp.Attr$2.inst(Attr.java:2225) > at com.sun.tools.javac.comp.Infer.instantiateExpr(Infer.java:333) > at com.sun.tools.javac.comp.Check.instantiatePoly(Check.java:453) > at com.sun.tools.javac.comp.Check.checkType(Check.java:412) > at com.sun.tools.javac.comp.Check.checkType(Check.java:405) > at com.sun.tools.javac.comp.Attr.visitLambda(Attr.java:2142) > at com.sun.tools.javac.tree.JCTree$JCLambda.accept(JCTree.java:1497) > at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:457) > at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:475) > at com.sun.tools.javac.comp.Attr.attribExpr(Attr.java:479) > at com.sun.tools.javac.comp.Attr.visitVarDef(Attr.java:877) > at com.sun.tools.javac.tree.JCTree$JCVariableDecl.accept(JCTree.java:745) > at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:457) > at com.sun.tools.javac.comp.Attr.attribTree(Attr.java:475) > at com.sun.tools.javac.comp.Attr.attribStat(Attr.java:510) > at com.sun.tools.javac.comp.Attr.attribClassBody(Attr.java:3719) > at com.sun.tools.javac.comp.Attr.attribClass(Attr.java:3642) > at com.sun.tools.javac.comp.Attr.attribClass(Attr.java:3578) > at com.sun.tools.javac.main.JavaCompiler.attribute(JavaCompiler.java:1153) > at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:843) > at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:802) > at com.sun.tools.javac.main.Main.compile(Main.java:418) > at com.sun.tools.javac.main.Main.compile(Main.java:336) > at com.sun.tools.javac.main.Main.compile(Main.java:327) > at com.sun.tools.javac.Main.compile(Main.java:82) > at com.sun.tools.javac.Main.main(Main.java:67) > > > Cheers, > > Mark > > From markmahieu at gmail.com Wed Nov 10 17:59:29 2010 From: markmahieu at gmail.com (Mark Mahieu) Date: Thu, 11 Nov 2010 01:59:29 +0000 Subject: valid SAM type not accepted In-Reply-To: <4CC88503.4060908@oracle.com> References: <4CC88503.4060908@oracle.com> Message-ID: Maurizio, Dunno if this is of any use to you, but I think I've just tickled the same (or a similar) bug with a slightly different example, which reduces to: class DoubleVision { interface A extends Runnable {} interface B extends A, Runnable {} B b = #{}; } $ javac DoubleVision.java DoubleVision.java:6: invalid target type B for lambda conversion B b = #{}; ^ reason: the target type of a lambda conversion has multiple non-overriding abstract methods 1 error Mark On 27 Oct 2010, at 21:01, maurizio cimadamore wrote: > Could you point me to the definition of the Ordering class? > > Is it this? > > http://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Ordering.html > > Maurizio > > > On 27/10/2010 20:37, Thomas Jung wrote: >> Hi, >> >> I tried to convert my lambda examples to the new syntax and got the >> following error with the latest changes: >> >> class X{ >> private Ordering x = new Ordering(){ public int compare(T >> left, T right) { return 0; }}; //as documentation >> private Ordering y = #{l, r -> 0 }; >> } >> >> X.java:8: invalid target type Ordering for lambda conversion >> private Ordering y = #{l, r -> 0 }; ^ >> reason: the target type of a lambda conversion has multiple >> non-overriding abstract methods >> where T is a type-variable: >> T extends Object declared in class X >> >> I hope it?s not a dump error. >> >> Thomas >> > > From maurizio.cimadamore at oracle.com Thu Nov 11 06:48:09 2010 From: maurizio.cimadamore at oracle.com (maurizio.cimadamore at oracle.com) Date: Thu, 11 Nov 2010 14:48:09 +0000 Subject: hg: lambda/lambda/langtools: Bug fixes: Message-ID: <20101111144815.924C8478E6@hg.openjdk.java.net> Changeset: f5c76a5c84e3 Author: mcimadamore Date: 2010-11-11 14:47 +0000 URL: http://hg.openjdk.java.net/lambda/lambda/langtools/rev/f5c76a5c84e3 Bug fixes: *) Lambda expression not allowed in instance creation context *) Target-type inference failure with '? extends' wildcards leads to compiler error *) Interface methods in diamond shaped inheritance trees are counted twice during SAM conversion *) Conformance error: Resolve/Infer should use Types.isConvertible instead of Types.isAssignable for actuals vs. formals check ! src/share/classes/com/sun/tools/javac/code/Types.java ! src/share/classes/com/sun/tools/javac/comp/Attr.java ! src/share/classes/com/sun/tools/javac/comp/Infer.java ! src/share/classes/com/sun/tools/javac/comp/Resolve.java + test/tools/javac/lambda/LambdaConv12.java + test/tools/javac/lambda/LambdaConv13.java ! test/tools/javac/lambda/TargetType04.out + test/tools/javac/lambda/TargetType15.java From markmahieu at gmail.com Thu Nov 11 07:57:00 2010 From: markmahieu at gmail.com (Mark Mahieu) Date: Thu, 11 Nov 2010 15:57:00 +0000 Subject: hg: lambda/lambda/langtools: Bug fixes: In-Reply-To: <20101111144815.924C8478E6@hg.openjdk.java.net> References: <20101111144815.924C8478E6@hg.openjdk.java.net> Message-ID: <1AEA0739-042E-41C4-B23E-692181BBAC6B@gmail.com> Hi Maurizio, That seems to have fixed the bugs I encountered - nice one, thanks! Also, I just remembered that I should still have some old jtreg tests I wrote for CICE a while back [1], some of which will probably apply to lambda after tweaking the syntax; I think the rules around SAM types, effectively final and even optional parameter types are sufficiently similar. Would it be useful to you if I were to try them out against lambda at this point, or is it too early for such deliberate bug-hunting? Mark [1] http://markmahieu.blogspot.com/2008/01/cicearm-prototype.html On 11 Nov 2010, at 14:48, maurizio.cimadamore at oracle.com wrote: > Changeset: f5c76a5c84e3 > Author: mcimadamore > Date: 2010-11-11 14:47 +0000 > URL: http://hg.openjdk.java.net/lambda/lambda/langtools/rev/f5c76a5c84e3 > > Bug fixes: > *) Lambda expression not allowed in instance creation context > *) Target-type inference failure with '? extends' wildcards leads to compiler error > *) Interface methods in diamond shaped inheritance trees are counted twice during SAM conversion > *) Conformance error: Resolve/Infer should use Types.isConvertible instead of Types.isAssignable for actuals vs. formals check > > ! src/share/classes/com/sun/tools/javac/code/Types.java > ! src/share/classes/com/sun/tools/javac/comp/Attr.java > ! src/share/classes/com/sun/tools/javac/comp/Infer.java > ! src/share/classes/com/sun/tools/javac/comp/Resolve.java > + test/tools/javac/lambda/LambdaConv12.java > + test/tools/javac/lambda/LambdaConv13.java > ! test/tools/javac/lambda/TargetType04.out > + test/tools/javac/lambda/TargetType15.java > > From maurizio.cimadamore at oracle.com Thu Nov 11 08:20:49 2010 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Thu, 11 Nov 2010 16:20:49 +0000 Subject: hg: lambda/lambda/langtools: Bug fixes: In-Reply-To: <1AEA0739-042E-41C4-B23E-692181BBAC6B@gmail.com> References: <20101111144815.924C8478E6@hg.openjdk.java.net> <1AEA0739-042E-41C4-B23E-692181BBAC6B@gmail.com> Message-ID: <4CDC17E1.9080409@oracle.com> On 11/11/10 15:57, Mark Mahieu wrote: > Hi Maurizio, > > That seems to have fixed the bugs I encountered - nice one, thanks! > > Also, I just remembered that I should still have some old jtreg tests > I wrote for CICE a while back [1], some of which will probably apply > to lambda after tweaking the syntax; I think the rules around SAM > types, effectively final and even optional parameter types are > sufficiently similar. Would it be useful to you if I were to try them > out against lambda at this point, or is it too early for such > deliberate bug-hunting? Well, it's definitively a good idea to give your tests a try. It's a shame we don't have a proper bug database set up (handling incoming bugs through emails doesn't scale very well, as it relies a lot on the memory of the reader ;-) ). On the other hand, I think it's best if we wait a little bit more - as there are some additional changes that I'd like to make to target-type inference that are likely to change the space of compilable programs a little bit. Thanks again Maurizio > > Mark > > [1] http://markmahieu.blogspot.com/2008/01/cicearm-prototype.html > > > On 11 Nov 2010, at 14:48, maurizio.cimadamore at oracle.com > wrote: > >> Changeset: f5c76a5c84e3 >> Author: mcimadamore >> Date: 2010-11-11 14:47 +0000 >> URL: http://hg.openjdk.java.net/lambda/lambda/langtools/rev/f5c76a5c84e3 >> >> Bug fixes: >> *) Lambda expression not allowed in instance creation context >> *) Target-type inference failure with '? extends' wildcards leads to >> compiler error >> *) Interface methods in diamond shaped inheritance trees are counted >> twice during SAM conversion >> *) Conformance error: Resolve/Infer should use Types.isConvertible >> instead of Types.isAssignable for actuals vs. formals check >> >> ! src/share/classes/com/sun/tools/javac/code/Types.java >> ! src/share/classes/com/sun/tools/javac/comp/Attr.java >> ! src/share/classes/com/sun/tools/javac/comp/Infer.java >> ! src/share/classes/com/sun/tools/javac/comp/Resolve.java >> + test/tools/javac/lambda/LambdaConv12.java >> + test/tools/javac/lambda/LambdaConv13.java >> ! test/tools/javac/lambda/TargetType04.out >> + test/tools/javac/lambda/TargetType15.java >> >> > From markmahieu at gmail.com Thu Nov 11 09:15:05 2010 From: markmahieu at gmail.com (Mark Mahieu) Date: Thu, 11 Nov 2010 17:15:05 +0000 Subject: hg: lambda/lambda/langtools: Bug fixes: In-Reply-To: <4CDC17E1.9080409@oracle.com> References: <20101111144815.924C8478E6@hg.openjdk.java.net> <1AEA0739-042E-41C4-B23E-692181BBAC6B@gmail.com> <4CDC17E1.9080409@oracle.com> Message-ID: On 11 November 2010 16:20, Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > On 11/11/10 15:57, Mark Mahieu wrote: > > Hi Maurizio, > > That seems to have fixed the bugs I encountered - nice one, thanks! > > Also, I just remembered that I should still have some old jtreg tests I > wrote for CICE a while back [1], some of which will probably apply to lambda > after tweaking the syntax; I think the rules around SAM types, effectively > final and even optional parameter types are sufficiently similar. Would it > be useful to you if I were to try them out against lambda at this point, or > is it too early for such deliberate bug-hunting? > > Well, it's definitively a good idea to give your tests a try. It's a shame > we don't have a proper bug database set up (handling incoming bugs through > emails doesn't scale very well, as it relies a lot on the memory of the > reader ;-) ). On the other hand, I think it's best if we wait a little bit > more - as there are some additional changes that I'd like to make to > target-type inference that are likely to change the space of compilable > programs a little bit. > > Thanks again > Maurizio > > OK, cool - I'll wait until your changes are in. Cheers, Mark From aruld at acm.org Thu Nov 11 20:21:17 2010 From: aruld at acm.org (Arul Dhesiaseelan) Date: Thu, 11 Nov 2010 21:21:17 -0700 Subject: Lambda conversion in Method invocation context Message-ID: Hi, I get the following compile error when I use a lambda in a method invocation context: SimpleRunnable.java:14: internal error; cannot instantiate println(String) at ExecutorService to (<>#void()) executor.submit(#{System.out.println("Processing a short-lived asynchronous task.")}); ^ 1 error final ExecutorService executor = Executors.newCachedThreadPool(); Runnable task = #{ System.out.println("Processing a short-lived asynchronous task.") };//compiles executor.submit(task); task = (Runnable)#{System.out.println("Processing a short-lived asynchronous task.")};//compiles executor.submit(task); executor.submit(#{System.out.println("Processing a short-lived asynchronous task.")});//fails I am not sure if this is supported in the compiler yet. Just thought of checking on the mailing list. -Arul From peter.levart at marand.si Thu Nov 11 23:11:07 2010 From: peter.levart at marand.si (Peter Levart) Date: Fri, 12 Nov 2010 08:11:07 +0100 Subject: Lambda conversion in Method invocation context In-Reply-To: References: Message-ID: <201011120811.07602.peter.levart@marand.si> On 11/12/10, Arul Dhesiaseelan wrote: > Hi, > > I get the following compile error when I use a lambda in a method invocation > context: > > SimpleRunnable.java:14: internal error; cannot instantiate println(String) > at ExecutorService to (<>#void()) > executor.submit(#{System.out.println("Processing a > short-lived asynchronous task.")}); > ^ > 1 error > I think, you're trying to write this: executor.submit( #{ System.out.println("Processing a short-lived asynchronous task."); } ); ...note the semicolon after println method invocation. Invocation of a void method can not be used as expression. Javac error is somewhat strange though. Peter From reinier at zwitserloot.com Fri Nov 12 01:24:09 2010 From: reinier at zwitserloot.com (Reinier Zwitserloot) Date: Fri, 12 Nov 2010 10:24:09 +0100 Subject: Lambdas and serialization In-Reply-To: References: <4CBDA6E7.1050501@oracle.com> <4CBDBCC1.9020706@univ-mlv.fr> <4CBDBC13.8060601@oracle.com> <4CBDE118.4030103@univ-mlv.fr> <4CBF15BA.4020703@univ-mlv.fr> <4CBF36B3.8020203@univ-mlv.fr> <4CC80634.5000908@oracle.com> Message-ID: Llewelyn, the equals method of comparator obviously compares.... the comparator. Not objects in it. That would require a signature of equals(T a, T B) which is something entirely different. And not in comparator today (and thus, not in comparator, ever, or backwards compatibility would be broken). As Greg said, A comparator need not be in sync with the T's own equals method. However, if a class implements Comparable (a totally different beast), then its own compareTo(other) method needs to return 0 when .equals() says true. --Reinier Zwitserloot On Wed, Nov 10, 2010 at 3:29 PM, Alex Blewitt wrote: > That can't be right. Consider a PersonComparator which just compares first > names. There may be two Alexs, but that doesn't mean they are the same > Person. > > Sent from my iPhone 4 > > On 10 Nov 2010, at 13:16, Llewellyn Falco wrote: > > > I just realized this is a mistake! > > > > Comparable will not work properly without the Equals method > > being overridden. > > > > Namely, if you have two object a, b you will get > > a.compareTo(b) == 0 > > but > > a.equals(b) == false > > > > depending on your implementation this will fail! > > > > Llewellyn Falco > > http:\\bit.ly\lambdas > > > > On Wed, Oct 27, 2010 at 4:00 AM, Maurizio Cimadamore < > > maurizio.cimadamore at oracle.com> wrote: > > > >> > >> btw: what happens with comparator? it's not a SAM (2 methods - compare, > >>> equals), but seems to be a very wanted case for lambdas? > >>> > >>> > >>> > >> The definition of SAM does not take into account methods inherithed from > >> java.lang.Object. Which means that Comparator really has just one > >> relevant target method and therefore can be used as the target of a SAM > >> conversion. > >> > >> Maurizio > >> > > > > From maurizio.cimadamore at oracle.com Fri Nov 12 01:28:24 2010 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 12 Nov 2010 09:28:24 +0000 Subject: Lambda conversion in Method invocation context In-Reply-To: <201011120811.07602.peter.levart@marand.si> References: <201011120811.07602.peter.levart@marand.si> Message-ID: <4CDD08B8.4000000@oracle.com> On 12/11/10 07:11, Peter Levart wrote: > On 11/12/10, Arul Dhesiaseelan wrote: > >> Hi, >> >> I get the following compile error when I use a lambda in a method invocation >> context: >> >> SimpleRunnable.java:14: internal error; cannot instantiate println(String) >> at ExecutorService to (<>#void()) >> executor.submit(#{System.out.println("Processing a >> short-lived asynchronous task.")}); >> ^ >> 1 error >> >> > I think, you're trying to write this: > > executor.submit( > #{ System.out.println("Processing a short-lived asynchronous task."); } > ); > > ...note the semicolon after println method invocation. Invocation of a void method can not be used as expression. > > Javac error is somewhat strange though. > > Peter > > The semi-colon should not represent a problem. The real problem is that the call is ambiguous: ExecutorService defines two methods called 'submit', one accepting Callable(T) and another one accepting a Runnable. Both target methods are compatible with a lambda whose type is 'void()'. The error message is weird because the prototype has some problems related to error recovery during overload resolution (which I'm trying to fix) - the version I'm working on emits this error: /home/maurizio/Desktop/Inference.java:6: reference to submit is ambiguous, both method submit(Callable) in ExecutorService and method submit(Runnable) in ExecutorService match e.submit(#{System.out.println("Processing a short-lived asynchronous task.")});//fails ^ where T is a type-variable: T extends Object declared in method submit(Callable) 1 error Which does a better job at explaining what's wrong. Maurizio From thomas.andreas.jung at googlemail.com Fri Nov 12 04:47:47 2010 From: thomas.andreas.jung at googlemail.com (Thomas Jung) Date: Fri, 12 Nov 2010 13:47:47 +0100 Subject: hg: lambda/lambda/langtools: Bug fixes: In-Reply-To: <4CDC17E1.9080409@oracle.com> References: <20101111144815.924C8478E6@hg.openjdk.java.net> <1AEA0739-042E-41C4-B23E-692181BBAC6B@gmail.com> <4CDC17E1.9080409@oracle.com> Message-ID: > It's a shame we don't have a proper bug database set up (handling incoming bugs > through emails doesn't scale very well, as it relies a lot on the memory > of the reader ;-) ). +1 for the bug database Thomas From maurizio.cimadamore at oracle.com Fri Nov 12 04:49:38 2010 From: maurizio.cimadamore at oracle.com (maurizio.cimadamore at oracle.com) Date: Fri, 12 Nov 2010 12:49:38 +0000 Subject: hg: lambda/lambda/langtools: Target-type inference fixes: Message-ID: <20101112124944.9213247937@hg.openjdk.java.net> Changeset: 9a1ae3fc0a88 Author: mcimadamore Date: 2010-11-12 12:49 +0000 URL: http://hg.openjdk.java.net/lambda/lambda/langtools/rev/9a1ae3fc0a88 Target-type inference fixes: *) Outcome of target-type inference depends on the context in which the lambda is used (assignment vs. method invocation context) when it shouldn't *) Trial attribution of lambda body leaves stale results in AST in case of overload resolution which leads to bad resolution and spurious diagnostics *) Spurious inference errors when synthetic types of lambda parameters depend on uninferred generic method type-args ! src/share/classes/com/sun/tools/javac/code/Type.java ! src/share/classes/com/sun/tools/javac/comp/Attr.java ! src/share/classes/com/sun/tools/javac/comp/Check.java ! src/share/classes/com/sun/tools/javac/comp/Infer.java ! src/share/classes/com/sun/tools/javac/comp/Resolve.java ! test/tools/javac/lambda/TargetType01.out ! test/tools/javac/lambda/TargetType15.java + test/tools/javac/lambda/TargetType16.java + test/tools/javac/lambda/TargetType16.out From peter.levart at marand.si Fri Nov 12 05:28:11 2010 From: peter.levart at marand.si (Peter Levart) Date: Fri, 12 Nov 2010 14:28:11 +0100 Subject: Lambda conversion in Method invocation context In-Reply-To: <4CDD08B8.4000000@oracle.com> References: <201011120811.07602.peter.levart@marand.si> <4CDD08B8.4000000@oracle.com> Message-ID: <201011121428.11268.peter.levart@marand.si> > > I think, you're trying to write this: > > > > executor.submit( > > #{ System.out.println("Processing a short-lived asynchronous task."); } > > ); > > > > ...note the semicolon after println method invocation. Invocation of a void method can not be used as expression. > > On 11/12/10, Maurizio Cimadamore wrote: > The semi-colon should not represent a problem. Oh, so this is valid code? Runnable r = #{ System.out.println() }; Peter From maurizio.cimadamore at oracle.com Fri Nov 12 06:08:44 2010 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 12 Nov 2010 14:08:44 +0000 Subject: Lambda conversion in Method invocation context In-Reply-To: <201011121428.11268.peter.levart@marand.si> References: <201011120811.07602.peter.levart@marand.si> <4CDD08B8.4000000@oracle.com> <201011121428.11268.peter.levart@marand.si> Message-ID: <4CDD4A6C.7050705@oracle.com> On 12/11/10 13:28, Peter Levart wrote: >>> I think, you're trying to write this: >>> >>> executor.submit( >>> #{ System.out.println("Processing a short-lived asynchronous task."); } >>> ); >>> >>> ...note the semicolon after println method invocation. Invocation of a void method can not be used as expression. >>> >>> > On 11/12/10, Maurizio Cimadamore wrote: > >> The semi-colon should not represent a problem. >> > Oh, so this is valid code? > > Runnable r = #{ System.out.println() }; > Yes it is. A method invocation is also an expression so it is possible to exploit the lambda expression syntax (w/o semicolon). Maurizio > > Peter > From maurizio.cimadamore at oracle.com Fri Nov 12 07:50:09 2010 From: maurizio.cimadamore at oracle.com (maurizio.cimadamore at oracle.com) Date: Fri, 12 Nov 2010 15:50:09 +0000 Subject: hg: lambda/lambda/langtools: Conversion support clean-up. Message-ID: <20101112155013.82B6E4793F@hg.openjdk.java.net> Changeset: 81019709553d Author: mcimadamore Date: 2010-11-12 15:49 +0000 URL: http://hg.openjdk.java.net/lambda/lambda/langtools/rev/81019709553d Conversion support clean-up. Got rid of the ConversionResult class and simplified both Types.isAssignable/Types.isConvertible. Now an unchecked exception is used in order to propagate a detailed diagnostic message about what went wrong during a conversion --- this is more in sync with the rest of the javac code. ! src/share/classes/com/sun/tools/apt/mirror/util/TypesImpl.java ! src/share/classes/com/sun/tools/javac/code/Type.java ! src/share/classes/com/sun/tools/javac/code/Types.java ! src/share/classes/com/sun/tools/javac/comp/Check.java ! src/share/classes/com/sun/tools/javac/comp/Infer.java ! src/share/classes/com/sun/tools/javac/comp/Resolve.java ! src/share/classes/com/sun/tools/javac/model/JavacTypes.java From aruld at acm.org Fri Nov 12 09:42:48 2010 From: aruld at acm.org (Arul Dhesiaseelan) Date: Fri, 12 Nov 2010 10:42:48 -0700 Subject: Lambda conversion in Method invocation context In-Reply-To: <4CDD08B8.4000000@oracle.com> References: <201011120811.07602.peter.levart@marand.si> <4CDD08B8.4000000@oracle.com> Message-ID: Hi Maurizio, Thanks for the clarification. The new error message is much better and clarifies the intent. -Arul On Fri, Nov 12, 2010 at 2:28 AM, Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > On 12/11/10 07:11, Peter Levart wrote: > > On 11/12/10, Arul Dhesiaseelan wrote: > > > >> Hi, > >> > >> I get the following compile error when I use a lambda in a method > invocation > >> context: > >> > >> SimpleRunnable.java:14: internal error; cannot instantiate > println(String) > >> at ExecutorService to (<>#void()) > >> executor.submit(#{System.out.println("Processing a > >> short-lived asynchronous task.")}); > >> ^ > >> 1 error > >> > >> > > I think, you're trying to write this: > > > > executor.submit( > > #{ System.out.println("Processing a short-lived asynchronous task."); > } > > ); > > > > ...note the semicolon after println method invocation. Invocation of a > void method can not be used as expression. > > > > Javac error is somewhat strange though. > > > > Peter > > > > > The semi-colon should not represent a problem. The real problem is that > the call is ambiguous: ExecutorService defines two methods called > 'submit', one accepting Callable(T) and another one accepting a > Runnable. Both target methods are compatible with a lambda whose type is > 'void()'. > > The error message is weird because the prototype has some problems > related to error recovery during overload resolution (which I'm trying > to fix) - the version I'm working on emits this error: > > /home/maurizio/Desktop/Inference.java:6: reference to submit is > ambiguous, both method submit(Callable) in ExecutorService and > method submit(Runnable) in ExecutorService match > e.submit(#{System.out.println("Processing a short-lived asynchronous > task.")});//fails > ^ > where T is a type-variable: > T extends Object declared in method submit(Callable) > 1 error > > Which does a better job at explaining what's wrong. > > Maurizio > > -- http://aruld.info http://twitter.com/aruld From maurizio.cimadamore at oracle.com Fri Nov 12 09:55:19 2010 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Fri, 12 Nov 2010 17:55:19 +0000 Subject: lambda finder available! Message-ID: <4CDD7F87.9040801@oracle.com> Hi, I'd like to spend few time in order to discuss a feature that I pushed while back and that probably went unnoticed (I just realized that the commit message didn't contain anything too specific about that :-) ). The idea is to find all potential anonymous inner class creation expressions that can be potentially replaced by a lambda expression. In order to turn this 'lambda finder' feature on, you need to pass the compiler the hidden flag '-XDidentifyLambdaCandidate=true'. This is an example of how the flag works; assume that you have the following code: class Test { Runnable r = new Runnable() { void run() { System.out.println("Hello!"); } }; } if you compile the above program with the hidden flag, javac will emit the following note: Test.java:2: Note: This anonymous inner class creation can be turned into a lambda expression. Runnable r = new Runnable() { public void run() { System.out.println("Hello!"); } }; ^ This means that this feature can be helpful (I hope) in order to refactor existing code base to use lambda expressions. Maurizio From thomas.andreas.jung at googlemail.com Fri Nov 12 22:09:24 2010 From: thomas.andreas.jung at googlemail.com (Thomas Jung) Date: Sat, 13 Nov 2010 07:09:24 +0100 Subject: lambda finder available! In-Reply-To: <4CDD7F87.9040801@oracle.com> References: <4CDD7F87.9040801@oracle.com> Message-ID: Hi Maurizio, nice feature. Could the message also contain the lambda expression? I've no idea about the compiler internals. Can you generate java code from the AST at this stage? Hopefully IDEs will support batch conversions when lambda expressions are in Java. Thomas On 12 November 2010 18:55, Maurizio Cimadamore wrote: > Hi, > I'd like to spend few time in order to discuss a feature that I pushed > while back and that probably went unnoticed (I just realized that the > commit message didn't contain anything too specific about that :-) ). > > The idea is to find all potential anonymous inner class creation > expressions that can be potentially replaced by a lambda expression. In > order to turn this 'lambda finder' feature on, you need to pass the > compiler the hidden flag '-XDidentifyLambdaCandidate=true'. > > This is an example of how the flag works; assume that you have the > following code: > > class Test { > Runnable r = new Runnable() { void run() { System.out.println("Hello!"); > } }; > } > > if you compile the above program with the hidden flag, javac will emit > the following note: > > Test.java:2: Note: This anonymous inner class creation can be turned > into a lambda expression. > Runnable r = new Runnable() { public void run() { > System.out.println("Hello!"); } }; > ? ? ? ? ? ? ? ? ? ? ? ? ? ? ^ > > This means that this feature can be helpful (I hope) in order to > refactor existing code base to use lambda expressions. > > Maurizio > > > From maurizio.cimadamore at oracle.com Sat Nov 13 02:46:49 2010 From: maurizio.cimadamore at oracle.com (maurizio cimadamore) Date: Sat, 13 Nov 2010 10:46:49 +0000 Subject: lambda finder available! In-Reply-To: References: <4CDD7F87.9040801@oracle.com> Message-ID: <4CDE6C99.9080807@oracle.com> On 13/11/2010 06:09, Thomas Jung wrote: > Hi Maurizio, > > nice feature. Could the message also contain the lambda expression? > I've no idea about the compiler internals. Can you generate java code > from the AST at this stage? Nice idea, I will think about it - it certainly looks feasible. > Hopefully IDEs will support batch conversions when lambda expressions > are in Java. Yeah - the compiler note can be used by IDE such as NetBeans to perform/suggest refactoring (similar to what we have done in project coin for diamond, multicatch and strings in switch). Maurizio > Thomas > > On 12 November 2010 18:55, Maurizio Cimadamore > wrote: >> Hi, >> I'd like to spend few time in order to discuss a feature that I pushed >> while back and that probably went unnoticed (I just realized that the >> commit message didn't contain anything too specific about that :-) ). >> >> The idea is to find all potential anonymous inner class creation >> expressions that can be potentially replaced by a lambda expression. In >> order to turn this 'lambda finder' feature on, you need to pass the >> compiler the hidden flag '-XDidentifyLambdaCandidate=true'. >> >> This is an example of how the flag works; assume that you have the >> following code: >> >> class Test { >> Runnable r = new Runnable() { void run() { System.out.println("Hello!"); >> } }; >> } >> >> if you compile the above program with the hidden flag, javac will emit >> the following note: >> >> Test.java:2: Note: This anonymous inner class creation can be turned >> into a lambda expression. >> Runnable r = new Runnable() { public void run() { >> System.out.println("Hello!"); } }; >> ^ >> >> This means that this feature can be helpful (I hope) in order to >> refactor existing code base to use lambda expressions. >> >> Maurizio >> >> >> From aruld at acm.org Sat Nov 13 11:28:15 2010 From: aruld at acm.org (Arul Dhesiaseelan) Date: Sat, 13 Nov 2010 12:28:15 -0700 Subject: Protected variable access inside Lambda blocks Message-ID: Hi, I am trying to access a protected final defined in my SAM type from within a Lambda block, but it fails with "cannot find symbol" compile error with the prototype. Here is a simple test: public class ProtectedAccess { public static abstract class LambdaAccess { protected static final String DEFAULT = "DEFAULT"; public abstract T block(); } public static void main(String... args) throws Exception { LambdaAccess lambdaAccess = #{ return DEFAULT;//compile error: unable to access this protected constant }; lambdaAccess.block(); LambdaAccess anonymousAccess = new LambdaAccess() { public String block () { return DEFAULT;//works just fine in case of anonymous types } }; anonymousAccess.block(); } } I am not sure if this is a limitation or if I am doing something wrong here. -Arul From markmahieu at gmail.com Sat Nov 13 12:22:59 2010 From: markmahieu at gmail.com (Mark Mahieu) Date: Sat, 13 Nov 2010 20:22:59 +0000 Subject: Protected variable access inside Lambda blocks In-Reply-To: References: Message-ID: Hi, I think that's because lambda expressions are now lexically scoped - see section 7 in http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html All you need to do in this case is qualify your reference to 'DEFAULT', ie. use 'LambdaAccess.DEFAULT' instead. Regards, Mark On 13 November 2010 19:28, Arul Dhesiaseelan wrote: > Hi, > > I am trying to access a protected final defined in my SAM type from within > a > Lambda block, but it fails with "cannot find symbol" compile error with the > prototype. > > Here is a simple test: > > public class ProtectedAccess { > public static abstract class LambdaAccess { > protected static final String DEFAULT = "DEFAULT"; > public abstract T block(); > } > > public static void main(String... args) throws Exception { > LambdaAccess lambdaAccess = #{ > return DEFAULT;//compile error: unable to access this protected > constant > }; > lambdaAccess.block(); > > LambdaAccess anonymousAccess = new LambdaAccess() { > public String block () { > return DEFAULT;//works just fine in case of anonymous types > } > }; > anonymousAccess.block(); > } > > } > > I am not sure if this is a limitation or if I am doing something wrong > here. > > -Arul > > From peter.levart at gmail.com Sat Nov 13 12:30:44 2010 From: peter.levart at gmail.com (Peter Levart) Date: Sat, 13 Nov 2010 21:30:44 +0100 Subject: Protected variable access inside Lambda blocks In-Reply-To: References: Message-ID: <201011132130.45404.peter.levart@gmail.com> On Saturday, November 13, 2010 08:28:15 pm Arul Dhesiaseelan wrote: > Hi, > > I am trying to access a protected final defined in my SAM type from within > a Lambda block, but it fails with "cannot find symbol" compile error with > the prototype. > > Here is a simple test: > > public class ProtectedAccess { > public static abstract class LambdaAccess { > protected static final String DEFAULT = "DEFAULT"; > public abstract T block(); > } > > public static void main(String... args) throws Exception { > LambdaAccess lambdaAccess = #{ > return DEFAULT;//compile error: unable to access this protected > constant > }; > lambdaAccess.block(); > > LambdaAccess anonymousAccess = new LambdaAccess() { > public String block () { > return DEFAULT;//works just fine in case of anonymous types > } > }; > anonymousAccess.block(); > } > > } > > I am not sure if this is a limitation or if I am doing something wrong > here. > > -Arul As of 15.10.2010, the lambdas are lexically scoped (see: http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html). In other words, code inside lambda sees only the symbols as the code immediately outside the lambda expression. Peter From aruld at acm.org Sat Nov 13 16:17:37 2010 From: aruld at acm.org (Arul Dhesiaseelan) Date: Sat, 13 Nov 2010 17:17:37 -0700 Subject: Protected variable access inside Lambda blocks In-Reply-To: <201011132130.45404.peter.levart@gmail.com> References: <201011132130.45404.peter.levart@gmail.com> Message-ID: Thanks Mark and Peter for the clarification. -Arul On Sat, Nov 13, 2010 at 1:30 PM, Peter Levart wrote: > On Saturday, November 13, 2010 08:28:15 pm Arul Dhesiaseelan wrote: > > Hi, > > > > I am trying to access a protected final defined in my SAM type from > within > > a Lambda block, but it fails with "cannot find symbol" compile error with > > the prototype. > > > > Here is a simple test: > > > > public class ProtectedAccess { > > public static abstract class LambdaAccess { > > protected static final String DEFAULT = "DEFAULT"; > > public abstract T block(); > > } > > > > public static void main(String... args) throws Exception { > > LambdaAccess lambdaAccess = #{ > > return DEFAULT;//compile error: unable to access this > protected > > constant > > }; > > lambdaAccess.block(); > > > > LambdaAccess anonymousAccess = new LambdaAccess() > { > > public String block () { > > return DEFAULT;//works just fine in case of anonymous > types > > } > > }; > > anonymousAccess.block(); > > } > > > > } > > > > I am not sure if this is a limitation or if I am doing something wrong > > here. > > > > -Arul > > As of 15.10.2010, the lambdas are lexically scoped (see: > http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html). > In other words, code inside > lambda sees only the symbols as the code immediately outside the lambda > expression. > > Peter > > From markmahieu at gmail.com Sat Nov 13 19:15:17 2010 From: markmahieu at gmail.com (Mark Mahieu) Date: Sun, 14 Nov 2010 03:15:17 +0000 Subject: Protected variable access inside Lambda blocks In-Reply-To: References: <201011132130.45404.peter.levart@gmail.com> Message-ID: <679C60B6-1AD4-4E89-B120-13D0341CD0EF@gmail.com> Hmm, actually I think there might be a slight wrinkle to watch out for in the current code - if my understanding is correct, then this example should print the result of calling toString() on the instance of 'Lexical': class Lexical { Lexical() { Runnable r = #{ System.out.println(toString()); }; r.run(); } public static void main(String[] args) { new Lexical(); } } However, it's calling the Runnable's toString() instead (note the $1): $ java Lexical Lexical$1 at 3a3ee284 Many other cases do seem to work as I'd expect though, as will the above example if Lexical is made to override toString(). There's also an issue with 'this'... class NoSuchField { NoSuchField() { Runnable r = #{ Object o = this; }; r.run(); } public static void main(String[] args) { new NoSuchField(); } } ... which compiles but throws a NoSuchFieldError when run: $ java NoSuchField Exception in thread "main" java.lang.NoSuchFieldError: this at NoSuchField$1.run(NoSuchField.java:4) at NoSuchField.(NoSuchField.java:5) at NoSuchField.main(NoSuchField.java:9) Finally, calling getClass() in a lambda expression causes an AssertionError at compile time: class GetClass { Runnable r = #{ getClass(); }; } $ javac GetClass.java ... java.lang.AssertionError at com.sun.tools.javac.code.Types.rank(Types.java:2974) at com.sun.tools.javac.code.Types.rank(Types.java:2946) at com.sun.tools.javac.code.Symbol$TypeSymbol.precedes(Symbol.java:574) at com.sun.tools.javac.code.Types.union(Types.java:3108) at com.sun.tools.javac.code.Types.glb(Types.java:3370) at com.sun.tools.javac.code.Types.capture(Types.java:3620) at com.sun.tools.javac.comp.Attr.capture(Attr.java:3812) at com.sun.tools.javac.comp.Attr.visitApply(Attr.java:1565) at com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java:1344) ... Mark On 14 Nov 2010, at 00:17, Arul Dhesiaseelan wrote: > Thanks Mark and Peter for the clarification. > > -Arul > > On Sat, Nov 13, 2010 at 1:30 PM, Peter Levart wrote: > On Saturday, November 13, 2010 08:28:15 pm Arul Dhesiaseelan wrote: > > Hi, > > > > I am trying to access a protected final defined in my SAM type from within > > a Lambda block, but it fails with "cannot find symbol" compile error with > > the prototype. > > > > Here is a simple test: > > > > public class ProtectedAccess { > > public static abstract class LambdaAccess { > > protected static final String DEFAULT = "DEFAULT"; > > public abstract T block(); > > } > > > > public static void main(String... args) throws Exception { > > LambdaAccess lambdaAccess = #{ > > return DEFAULT;//compile error: unable to access this protected > > constant > > }; > > lambdaAccess.block(); > > > > LambdaAccess anonymousAccess = new LambdaAccess() { > > public String block () { > > return DEFAULT;//works just fine in case of anonymous types > > } > > }; > > anonymousAccess.block(); > > } > > > > } > > > > I am not sure if this is a limitation or if I am doing something wrong > > here. > > > > -Arul > > As of 15.10.2010, the lambdas are lexically scoped (see: > http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html). In other words, code inside > lambda sees only the symbols as the code immediately outside the lambda expression. > > Peter > > > > From ss at comp.lancs.ac.uk Mon Nov 15 00:55:07 2010 From: ss at comp.lancs.ac.uk (Steven Simpson) Date: Mon, 15 Nov 2010 08:55:07 +0000 Subject: Problem of defender methods with generic methods In-Reply-To: References: Message-ID: <4CE0F56B.4090101@comp.lancs.ac.uk> Hi, On 06/11/10 04:55, Ali Ebrahimi wrote: > public interface CustomizedList extends List { > > extension A max() default > Trait.max; <============= cannot find symbol A What is this supposed to mean, especially to a caller? AIUI, CustomizedList.max will appear to the caller as a regular virtual method, and the caller has no knowlegde that it is somehow bound to Trait.max. > public static class Trait { > public static > R max( > final CustomizedList self) { Okay, Trait.max is defined so that it is only valid on CustomizedLists of Comparables, though this isn't directly visible to the caller of CustomizedList.max. I suppose that the declaration of CustomizedList.max aims to indicate that it cannot be called if the element type is not Comparable, to ensure that the constraints on Trait.max will be met. So what's the full signature of CustomizedList.max? Simply stripping out the extension syntax leaves this: > A max(); But that doesn't place any extra restriction on , and would allow the caller to write: CustomizedList list = ...; String result = list.max(); // compiles However, when compiling CustomizedList, it is seen (approximately) that must correspond to , and that must correspond to , so must correspond to , so there's enough information in the declaration of CustomizedList.max to show that there are further constraints on - but this information is not directly available to the caller unless the compiler makes it available through the method's signature while it has the opportunity. But what can this new signature for CustomizedList.max be? AFAIK, there's no syntax that expresses additional constraints on the class's type parameters. I believe that redeclaring actually introduces a new which hides the class's , so it's no better than : > T max(); CustomizedList list = ...; String result = list.max(); // still compiles Has a 'reconstraining' syntax been introduced somewhere? If not, I think one is necessary to deal with these cases, if such cases are to be permitted. Cheers, Steven From maurizio.cimadamore at oracle.com Mon Nov 15 02:02:51 2010 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 15 Nov 2010 10:02:51 +0000 Subject: Problem of defender methods with generic methods In-Reply-To: <4CE0F56B.4090101@comp.lancs.ac.uk> References: <4CE0F56B.4090101@comp.lancs.ac.uk> Message-ID: <4CE1054B.1050701@oracle.com> On 15/11/10 08:55, Steven Simpson wrote: > Hi, > > On 06/11/10 04:55, Ali Ebrahimi wrote: > >> public interface CustomizedList extends List { >> > extension A max() default >> Trait.max;<============= cannot find symbol A >> > What is this supposed to mean, especially to a caller? AIUI, > CustomizedList.max will appear to the caller as a regular virtual > method, and the caller has no knowlegde that it is somehow bound to > Trait.max. > I think that the above signature wrong - the problems you see (i.e. the fact that you can call max on a List) stems from the fact that type-variable A and type-variable T are unrelated. From the caller's perspective, calling max() on a List should return T - which means that the signature of max() should be something like: public interface CustomizedList extends List { extension T max() default ... } Now let's take a look at the class Trait - since what we had is similar enough to Collections.max I think we should keep it: public static class Trait { public static> R max(CustomizedList self) { ... } } The only bit missing is: how are the extension method and the static Trait method linked together? The problem is they can't - at least not now: if you try to do this: public interface CustomizedList extends List { extension T max() default Trait.max() } You will get a compilation error, as T is not statically suitable for an R whose bound is Comparable. On the other hand, we are talking about adding methods to perform _comparisons_ on the contents of a collection - which seem to imply that the collection itself must contain elements which are some subtype of Comparable. Therefore, I think the right signature would be: public interface CustomizedList> extends List { extension T max() default Trait.max() } Which would compile w/o problems (and the right semantics will be enforced by the compiler - no new syntax required). Maurizio From ali.ebrahimi1781 at gmail.com Mon Nov 15 02:51:11 2010 From: ali.ebrahimi1781 at gmail.com (Ali Ebrahimi) Date: Mon, 15 Nov 2010 14:21:11 +0330 Subject: Problem of defender methods with generic methods In-Reply-To: <4CE1054B.1050701@oracle.com> References: <4CE0F56B.4090101@comp.lancs.ac.uk> <4CE1054B.1050701@oracle.com> Message-ID: Hi Maurizio, Comments inlined. On Mon, Nov 15, 2010 at 1:32 PM, Maurizio Cimadamore < maurizio.cimadamore at oracle.com> wrote: > On 15/11/10 08:55, Steven Simpson wrote: > > Hi, > > > > On 06/11/10 04:55, Ali Ebrahimi wrote: > > > >> public interface CustomizedList extends List { > >> > extension A max() default > >> Trait.max;<============= cannot find symbol A > >> > > What is this supposed to mean, especially to a caller? AIUI, > > CustomizedList.max will appear to the caller as a regular virtual > > method, and the caller has no knowlegde that it is somehow bound to > > Trait.max. > > > I think that the above signature wrong - the problems you see (i.e. the > fact that you can call max on a List) stems from the fact that > type-variable A and type-variable T are unrelated. > > The reason for this issue is current Compiler restriction. if compiler can accept the following syntax: > extension A max() default Trait.max; The aim is here to restrict calling of max only on CustomizedLists of Comparables, otherwise compiler raise compile time error. I know this is a new concept- a kind of conditional inheritance. but currently this can not be implemented in java, unless by introducing a new subinterface. then we have two interfaces. public interface ComparableList> extends CustomizedList { extension T max() default Trait.max() } public interface CustomizedList extends List { extension CustomizedList filter(Predicate predicate) default Trait.filter; extension CustomizedList map(Extrator predicate) default Trait.map; ... } From the caller's perspective, calling max() on a List should return > T - which means that the signature of max() should be something like: > > public interface CustomizedList extends List { > extension T max() default ... > } > > > Now let's take a look at the class Trait - since what we had is similar > enough to Collections.max I think we should keep it: > > public static class Trait { > public static> R > max(CustomizedList self) { ... } > } > > > The only bit missing is: how are the extension method and the static > Trait method linked together? The problem is they can't - at least not > now: if you try to do this: > > public interface CustomizedList extends List { > extension T max() default Trait.max() > } > > You will get a compilation error, as T is not statically suitable for an > R whose bound is Comparable. > > On the other hand, we are talking about adding methods to perform > _comparisons_ on the contents of a collection - which seem to imply that > the collection itself must contain elements which are some subtype of > Comparable. Therefore, I think the right signature would be: > > public interface CustomizedList> extends List > { > extension T max() default Trait.max() > } > > > > Which would compile w/o problems (and the right semantics will be > enforced by the compiler - no new syntax required). > > Maurizio > > Best Regards, Ali Ebrahimi From tronicek at fit.cvut.cz Mon Nov 15 04:56:45 2010 From: tronicek at fit.cvut.cz (=?utf-8?B?IlpkZW7Em2sgVHJvbsOtxI1layI=?=) Date: Mon, 15 Nov 2010 13:56:45 +0100 Subject: lambda finder available! In-Reply-To: <4CDE6C99.9080807@oracle.com> References: <4CDD7F87.9040801@oracle.com> <4CDE6C99.9080807@oracle.com> Message-ID: RefactoringNG can help with such refactorings. It does not work with JDK 7 now but certainly will in future. http://kenai.com/projects/refactoringng/pages/Home Z. -- Zdenek Tronicek FIT CTU in Prague maurizio cimadamore napsal(a): > On 13/11/2010 06:09, Thomas Jung wrote: >> Hi Maurizio, >> >> nice feature. Could the message also contain the lambda expression? >> I've no idea about the compiler internals. Can you generate java code >> from the AST at this stage? > Nice idea, I will think about it - it certainly looks feasible. > >> Hopefully IDEs will support batch conversions when lambda expressions >> are in Java. > Yeah - the compiler note can be used by IDE such as NetBeans to > perform/suggest refactoring (similar to what we have done in project > coin for diamond, multicatch and strings in switch). > > Maurizio >> Thomas >> >> On 12 November 2010 18:55, Maurizio Cimadamore >> wrote: >>> Hi, >>> I'd like to spend few time in order to discuss a feature that I pushed >>> while back and that probably went unnoticed (I just realized that the >>> commit message didn't contain anything too specific about that :-) ). >>> >>> The idea is to find all potential anonymous inner class creation >>> expressions that can be potentially replaced by a lambda expression. In >>> order to turn this 'lambda finder' feature on, you need to pass the >>> compiler the hidden flag '-XDidentifyLambdaCandidate=true'. >>> >>> This is an example of how the flag works; assume that you have the >>> following code: >>> >>> class Test { >>> Runnable r = new Runnable() { void run() { >>> System.out.println("Hello!"); >>> } }; >>> } >>> >>> if you compile the above program with the hidden flag, javac will emit >>> the following note: >>> >>> Test.java:2: Note: This anonymous inner class creation can be turned >>> into a lambda expression. >>> Runnable r = new Runnable() { public void run() { >>> System.out.println("Hello!"); } }; >>> ^ >>> >>> This means that this feature can be helpful (I hope) in order to >>> refactor existing code base to use lambda expressions. >>> >>> Maurizio >>> >>> >>> > > > From ss at comp.lancs.ac.uk Mon Nov 15 05:46:27 2010 From: ss at comp.lancs.ac.uk (Steven Simpson) Date: Mon, 15 Nov 2010 13:46:27 +0000 Subject: Problem of defender methods with generic methods In-Reply-To: <4CE1054B.1050701@oracle.com> References: <4CE0F56B.4090101@comp.lancs.ac.uk> <4CE1054B.1050701@oracle.com> Message-ID: <4CE139B3.2020602@comp.lancs.ac.uk> On 15/11/10 10:02, Maurizio Cimadamore wrote: > On 15/11/10 08:55, Steven Simpson wrote: >> On 06/11/10 04:55, Ali Ebrahimi wrote: >> >>> public interface CustomizedList extends List { >>> > extension A max() default >>> Trait.max;<============= cannot find symbol A >>> >> What is this supposed to mean, especially to a caller? AIUI, >> CustomizedList.max will appear to the caller as a regular virtual >> method, and the caller has no knowlegde that it is somehow bound to >> Trait.max. >> > I think that the above signature wrong - the problems you see (i.e. > the fact that you can call max on a List) stems from the > fact that type-variable A and type-variable T are unrelated. Quite. That's sort-of the point I was making - it's an attempt to relate A and T. It failed when I tried it, and I wondered whether that form, or anything else, was mandated. [snip understood explanation] > On the other hand, we are talking about adding methods to perform > _comparisons_ on the contents of a collection - which seem to imply > that the collection itself must contain elements which are some > subtype of Comparable. Therefore, I think the right signature would be: > > public interface CustomizedList> extends > List { > extension T max() default Trait.max() > } Indeed, though this loses the advantage that a plain old Collections.max(list) has - the additional constraint isn't applied anywhere but the call site. You only need to apply the constraint if you want to call max, not every time you have a CustomizedList. Without this deferring of constraint checking (from class to method), we can't add max to List, so we're forced to use a subtype of List. Yet it seems unlikely that someone would devise an API (say) with: void doSomething(CustomizedList list) { ... } ...instead of: > void doSomething(List list) { ... } ...just so they could write list.max() instead of Collections.max(list), while forcing the caller to use a class other than List, which class doesn't actually bring much advantage to the caller. To force the caller in this way suggests that one should always rewrite List as CustomizedList, knowing that String is Comparable, just in case the list might eventually be submitted to an API that wants to call list.max() - which then implies further rewriting when another kind of List subtype comes along imposing further/alternative constraints on . It's more plausible, then, that the API writer would use one of the following options: 1. Just use List in the interface, with Collections.max(..) in the implementation. 2. As above, but with static extension methods (but still no dynamic dispatch on the max call itself). 3. Add a List defender that imposes additional constraints on , as Ali Ebrahimi seems to have tried to do. So, I guess I'm saying that not supporting option 3 could weaken the case for defenders, while acknowledging that, if option 3 were to be supported, there would have to be a way for methods of a generic class to impose additional constraints on their class's type parameters without having to apply those constraints to the entire class, and this information would have to be available at call sites to statically verify those constraints. Cheers, Steven From maurizio.cimadamore at oracle.com Mon Nov 15 09:08:16 2010 From: maurizio.cimadamore at oracle.com (maurizio.cimadamore at oracle.com) Date: Mon, 15 Nov 2010 17:08:16 +0000 Subject: hg: lambda/lambda/langtools: Lexical scope fixes: Message-ID: <20101115170824.21F79479FF@hg.openjdk.java.net> Changeset: 4335e2277cbd Author: mcimadamore Date: 2010-11-15 17:07 +0000 URL: http://hg.openjdk.java.net/lambda/lambda/langtools/rev/4335e2277cbd Lexical scope fixes: *) wrong dispatching of Object members (e.g. toString()) inside a lambda in case Object member not overriden in enclosing class *) javac crash when compiling a lambda expression containing an Object.getClass() call *) NoSuchField error when executing a lambda expression containing 'this' ! src/share/classes/com/sun/tools/javac/comp/Attr.java ! src/share/classes/com/sun/tools/javac/comp/Unlambda.java + test/tools/javac/lambda/LambdaScope03.java From howard.lovatt at gmail.com Mon Nov 15 14:06:58 2010 From: howard.lovatt at gmail.com (Howard Lovatt) Date: Tue, 16 Nov 2010 09:06:58 +1100 Subject: Problem of defender methods with generic methods Message-ID: @Ali, The best way to do this is to introduce an extra interface as you have suggested, you need to encode the information in a type (hence you need an extra type). Unfortunately you don't like this solution. However I think this is a good solution consistent with the rest of Java and in particular the concept of interfaces and I am therefore happy to see the proposed behaviour for extension methods. ? -- Howard. From alex.buckley at oracle.com Mon Nov 15 14:30:54 2010 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 15 Nov 2010 14:30:54 -0800 Subject: Problem of defender methods with generic methods In-Reply-To: <4CE139B3.2020602@comp.lancs.ac.uk> References: <4CE0F56B.4090101@comp.lancs.ac.uk> <4CE1054B.1050701@oracle.com> <4CE139B3.2020602@comp.lancs.ac.uk> Message-ID: <4CE1B49E.4030004@oracle.com> Hi Steven, On 11/15/2010 5:46 AM, Steven Simpson wrote: > It's more plausible, then, that the API writer would use one of the > following options: > > 1. Just use List in the interface, with > Collections.max(..) in the implementation. > 2. As above, but with static extension methods (but still no dynamic > dispatch on the max call itself). > 3. Add a List defender that imposes additional constraints on , as > Ali Ebrahimi seems to have tried to do. Just to clarify, for (2), do you mean adding an extension method to interface List: public interface List { > extension A max() default Collections.max; } (By "no dynamic dispatch on the max call", I presume you mean a scenario where there is no CustomizedList to override List's max.) And furthermore not introducing CustomizedList ? Alex From alex.buckley at oracle.com Mon Nov 15 14:50:32 2010 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 15 Nov 2010 14:50:32 -0800 Subject: Problem of defender methods with generic methods In-Reply-To: References: <4CE0F56B.4090101@comp.lancs.ac.uk> <4CE1054B.1050701@oracle.com> Message-ID: <4CE1B938.6050509@oracle.com> Hi Ali, On 11/15/2010 2:51 AM, Ali Ebrahimi wrote: > if compiler can accept the following syntax: > > > extension A max() default > Trait.max; > > The aim is here to restrict calling of max only on CustomizedLists of > Comparables, otherwise compiler raise compile time error. > > I know this is a new concept- a kind of conditional inheritance. but > currently this can not be implemented in java, unless by introducing a new > subinterface. then we have two interfaces. You and Steven have identified a valuable idiom that avoids new subtypes - we can call it "conditional inheritance" if you like - but unfortunately the generics mechanism cannot express it. Namely, the bound of a type variable can be _either_ another type variable _or_ a combination of classes and interfaces. (See JLS3 4.4.) The trouble is erasure: if a type variable's bound could be 'T & Comparable<...>', then it would not be clear whether to erase max's return type to Object (the erasure of T) or Comparable (the erasure of Comparable<...>). I guess we could pick the former, but the Signature attribute would want to record both T and Comparable<..> for later typechecking of A's witness from the call site. (Does it subtype List's type argument _and_ Comparable<..> ?) JLS3 4.4 arranged things to ensure a unique erasure, so it's not just a matter of relaxing syntax to allow your desired type variable for max. I personally believe we will want to add extension methods that further restrict the enclosing generic type's bounds, and that a new subinterface (ComparableList) will often be undesirable. But more experience is needed. Alex From ss at comp.lancs.ac.uk Mon Nov 15 16:32:12 2010 From: ss at comp.lancs.ac.uk (Steven Simpson) Date: Tue, 16 Nov 2010 00:32:12 +0000 Subject: Problem of defender methods with generic methods In-Reply-To: <4CE1B49E.4030004@oracle.com> References: <4CE0F56B.4090101@comp.lancs.ac.uk> <4CE1054B.1050701@oracle.com> <4CE139B3.2020602@comp.lancs.ac.uk> <4CE1B49E.4030004@oracle.com> Message-ID: <4CE1D10C.3060002@comp.lancs.ac.uk> Hi Alex, On 15/11/10 22:30, Alex Buckley wrote: > Hi Steven, > > On 11/15/2010 5:46 AM, Steven Simpson wrote: >> It's more plausible, then, that the API writer would use one of the >> following options: >> >> 1. Just use List in the interface, with >> Collections.max(..) in the implementation. >> 2. As above, but with static extension methods (but still no dynamic >> dispatch on the max call itself). >> 3. Add a List defender that imposes additional constraints on , as >> Ali Ebrahimi seems to have tried to do. > > Just to clarify, for (2), do you mean adding an extension method to > interface List: > > public interface List { > > > extension A max() > default Collections.max; > } No, I'm simply talking about the static alternative to extension methods, as C# has (AIUI), which are just syntactic sugar for a static method call. The problem shouldn't arise there, as the existence of an extension method like max() doesn't require that all Lists have its additional constraint. (1) and (2) are semantically equivalent. > (By "no dynamic dispatch on the max call", I presume you mean a > scenario where there is no CustomizedList to override List's max.) No, following on from above. With a static extension method, not a dynamic defender method, List implementations aren't able to override the default, because it's just a static call. I'm merely acknowledging that static extensions have their own problems. > And furthermore not introducing CustomizedList? Yes, all three options are intended to avoid having two interface types, e.g. Base and Derived, just because one of the extension methods has the extra constraint. Option 4 is to introduce a new type. ;-) (I've slightly transposed Ali's problem by imagining that he'd prefer to add these extensions directly to List, and that he's only introduced CustomizedList for the sake of toying with the new language features. I don't think that fundamentally alters the problem though, which is that two of his extensions work best with List, while the other one demands List.) Cheers! From alex.buckley at oracle.com Mon Nov 15 16:49:05 2010 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 15 Nov 2010 16:49:05 -0800 Subject: Problem of defender methods with generic methods In-Reply-To: <4CE1D10C.3060002@comp.lancs.ac.uk> References: <4CE0F56B.4090101@comp.lancs.ac.uk> <4CE1054B.1050701@oracle.com> <4CE139B3.2020602@comp.lancs.ac.uk> <4CE1B49E.4030004@oracle.com> <4CE1D10C.3060002@comp.lancs.ac.uk> Message-ID: <4CE1D501.90707@oracle.com> On 11/15/2010 4:32 PM, Steven Simpson wrote: > No, I'm simply talking about the static alternative to extension > methods, as C# has (AIUI), which are just syntactic sugar for a static > method call. The problem shouldn't arise there, as the existence of an > extension method like max() doesn't require that all Lists have its > additional constraint. (1) and (2) are > semantically equivalent. Ah, the benefits of _use-site_ extension methods. (Whether static or dynamic.) > (I've slightly transposed Ali's problem by imagining that he'd prefer to > add these extensions directly to List, and that he's only introduced > CustomizedList for the sake of toying with the new language features. I > don't think that fundamentally alters the problem though, which is that > two of his extensions work best with List, while the other one > demands List.) Exactly. Alex From ss at comp.lancs.ac.uk Mon Nov 15 16:52:10 2010 From: ss at comp.lancs.ac.uk (Steven Simpson) Date: Tue, 16 Nov 2010 00:52:10 +0000 Subject: Problem of defender methods with generic methods In-Reply-To: <4CE1B938.6050509@oracle.com> References: <4CE0F56B.4090101@comp.lancs.ac.uk> <4CE1054B.1050701@oracle.com> <4CE1B938.6050509@oracle.com> Message-ID: <4CE1D5BA.9080206@comp.lancs.ac.uk> On 15/11/10 22:50, Alex Buckley wrote: > You and Steven have identified a valuable idiom that avoids new subtypes > - we can call it "conditional inheritance" if you like - but > unfortunately the generics mechanism cannot express it. Namely, the > bound of a type variable can be _either_ another type variable _or_ a > combination of classes and interfaces. (See JLS3 4.4.) > > The trouble is erasure: if a type variable's bound could be 'T & > Comparable<...>', then it would not be clear whether to erase max's > return type to Object (the erasure of T) or Comparable (the erasure of > Comparable<...>). I guess we could pick the former, but the Signature > attribute would want to record both T and Comparable<..> for later > typechecking of A's witness from the call site. (Does it subtype List's > type argument _and_ Comparable<..> ?) Could it be done like this?: interface List { ... > extension T max() default Collections.max; } The generic method signature as it appears in the bytecode would be almost the same, but with some extra flag on T, e.g. '!': ;>()TT ...indicating that it's a refinement of the containing class's generic signature, not a completely separate T. These would both be available to the compiler to statically check each call. From alex.buckley at oracle.com Mon Nov 15 18:00:07 2010 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 15 Nov 2010 18:00:07 -0800 Subject: Problem of defender methods with generic methods In-Reply-To: <4CE1D5BA.9080206@comp.lancs.ac.uk> References: <4CE0F56B.4090101@comp.lancs.ac.uk> <4CE1054B.1050701@oracle.com> <4CE1B938.6050509@oracle.com> <4CE1D5BA.9080206@comp.lancs.ac.uk> Message-ID: <4CE1E5A7.6050206@oracle.com> On 11/15/2010 4:52 PM, Steven Simpson wrote: > Could it be done like this?: > > interface List { > ... > > extension T max() default Collections.max; > } > > The generic method signature as it appears in the bytecode would be > almost the same, but with some extra flag on T, e.g. '!': > > ;>()TT It could, but don't forget to document the impact of additional bounds on subtyping (notably, containment of type arguments) and inference. The real question is whether library evolution commonly needs to so restrict type variables in the first place. Alex P.S. A better use of the "new" token in the context of a bound is to mandate that witnesses of a type variable have a no-args constructor, so you can say "new T()" in the body. But that's another story. From ali.ebrahimi1781 at gmail.com Mon Nov 15 20:58:29 2010 From: ali.ebrahimi1781 at gmail.com (Ali Ebrahimi) Date: Tue, 16 Nov 2010 08:28:29 +0330 Subject: Problem of defender methods with generic methods In-Reply-To: <4CE1B938.6050509@oracle.com> References: <4CE0F56B.4090101@comp.lancs.ac.uk> <4CE1054B.1050701@oracle.com> <4CE1B938.6050509@oracle.com> Message-ID: Hi Alex, Comments inlined. On Tue, Nov 16, 2010 at 2:20 AM, Alex Buckley wrote: > Hi Ali, > > > On 11/15/2010 2:51 AM, Ali Ebrahimi wrote: > >> if compiler can accept the following syntax: >> >> > extension A max() default >> Trait.max; >> >> The aim is here to restrict calling of max only on CustomizedLists of >> Comparables, otherwise compiler raise compile time error. >> >> I know this is a new concept- a kind of conditional inheritance. but >> currently this can not be implemented in java, unless by introducing a new >> subinterface. then we have two interfaces. >> > > You and Steven have identified a valuable idiom that avoids new subtypes - > we can call it "conditional inheritance" if you like - but unfortunately the > generics mechanism cannot express it. Namely, the bound of a type variable > can be _either_ another type variable _or_ a combination of classes and > interfaces. (See JLS3 4.4.) > > The trouble is erasure: if a type variable's bound could be 'T & > Comparable<...>', then it would not be clear whether to erase max's return > type to Object (the erasure of T) or Comparable (the erasure of > Comparable<...>). I guess we could pick the former, but the I prefer later, since Comparable implicitly is subtype of Object. > Signature attribute would want to record both T and Comparable<..> for > later typechecking of A's witness from the call site. (Does it subtype > List's type argument _and_ Comparable<..> ?) > > JLS3 4.4 arranged things to ensure a unique erasure, so it's not just a > matter of relaxing syntax to allow your desired type variable for max. > > I personally believe we will want to add extension methods that further > restrict the enclosing generic type's bounds, and that a new subinterface > (ComparableList) will often be undesirable. But more > experience is needed. > > Alex > Best Regards, Ali Ebrahimi From alex.buckley at oracle.com Mon Nov 15 21:29:24 2010 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 15 Nov 2010 21:29:24 -0800 Subject: Problem of defender methods with generic methods In-Reply-To: References: <4CE0F56B.4090101@comp.lancs.ac.uk> <4CE1054B.1050701@oracle.com> <4CE1B938.6050509@oracle.com> Message-ID: <4CE216B4.7040608@oracle.com> On 11/15/2010 8:58 PM, Ali Ebrahimi wrote: > I prefer later, since Comparable implicitly is subtype of Object. Yes, but this is a special case. In general, the bound of T (from enclosing type List) is not Object, and the auxiliary interfaces do not exist on that bound. (Imagine ) Alex From peter.levart at marand.si Mon Nov 15 23:35:01 2010 From: peter.levart at marand.si (Peter Levart) Date: Tue, 16 Nov 2010 08:35:01 +0100 Subject: Problem of defender methods with generic methods In-Reply-To: <4CE1B938.6050509@oracle.com> References: <4CE1B938.6050509@oracle.com> Message-ID: <201011160835.02197.peter.levart@marand.si> On 11/15/10, Alex Buckley wrote: > Hi Ali, > > On 11/15/2010 2:51 AM, Ali Ebrahimi wrote: > > if compiler can accept the following syntax: > > > > > extension A max() default > > Trait.max; > > > > The aim is here to restrict calling of max only on CustomizedLists of > > Comparables, otherwise compiler raise compile time error. > > > > I know this is a new concept- a kind of conditional inheritance. but > > currently this can not be implemented in java, unless by introducing a new > > subinterface. then we have two interfaces. > > You and Steven have identified a valuable idiom that avoids new subtypes > - we can call it "conditional inheritance" if you like - but > unfortunately the generics mechanism cannot express it. Namely, the > bound of a type variable can be _either_ another type variable _or_ a > combination of classes and interfaces. (See JLS3 4.4.) > > The trouble is erasure: if a type variable's bound could be 'T & > Comparable<...>', then it would not be clear whether to erase max's > return type to Object (the erasure of T) or Comparable (the erasure of > Comparable<...>). I guess we could pick the former, but the Signature > attribute would want to record both T and Comparable<..> for later > typechecking of A's witness from the call site. (Does it subtype List's > type argument _and_ Comparable<..> ?) > > JLS3 4.4 arranged things to ensure a unique erasure, so it's not just a > matter of relaxing syntax to allow your desired type variable for max. > > I personally believe we will want to add extension methods that further > restrict the enclosing generic type's bounds, and that a new > subinterface (ComparableList) will often be undesirable. > But more experience is needed. > > Alex Why are extension methods so special? Wouldn't also other kinds of methods and class constructors benefit from such a feature? Take for example the interface SortedSet and one of it's implementations: TreeSet. It would be desirable that constructors not taking a Comparator restrict type variable to a subtype of Comparable. Regards, Peter > > From peter.levart at marand.si Tue Nov 16 00:43:23 2010 From: peter.levart at marand.si (Peter Levart) Date: Tue, 16 Nov 2010 09:43:23 +0100 Subject: Problem of defender methods with generic methods In-Reply-To: <4CE1E5A7.6050206@oracle.com> References: <4CE1D5BA.9080206@comp.lancs.ac.uk> <4CE1E5A7.6050206@oracle.com> Message-ID: <201011160943.23514.peter.levart@marand.si> On 11/16/10, Alex Buckley wrote: > On 11/15/2010 4:52 PM, Steven Simpson wrote: > > Could it be done like this?: > > > > interface List { > > ... > > > extension T max() default Collections.max; > > } I would expect that 'new' is actualy implicit in generic method declarations. Every type variable currently declared in a generic method is it's own instance (i.e. new). What we might need is a syntax that says "this": interface List { ... > extension T max() default Collections.max; } // which would mean that for a particular method, the type variable of the type instance should be bound accordingly or compile-time error is raised at a call site. > > > > The generic method signature as it appears in the bytecode would be > > almost the same, but with some extra flag on T, e.g. '!': > > > > ;>()TT > > It could, but don't forget to document the impact of additional bounds > on subtyping (notably, containment of type arguments) and inference. The > real question is whether library evolution commonly needs to so restrict > type variables in the first place. > > Alex > > P.S. A better use of the "new" token in the context of a bound is to > mandate that witnesses of a type variable have a no-args constructor, so > you can say "new T()" in the body. But that's another story. > > From alex.buckley at oracle.com Tue Nov 16 10:58:21 2010 From: alex.buckley at oracle.com (Alex Buckley) Date: Tue, 16 Nov 2010 10:58:21 -0800 Subject: Problem of defender methods with generic methods In-Reply-To: <201011160835.02197.peter.levart@marand.si> References: <4CE1B938.6050509@oracle.com> <201011160835.02197.peter.levart@marand.si> Message-ID: <4CE2D44D.4010205@oracle.com> On 11/15/2010 11:35 PM, Peter Levart wrote: > Why are extension methods so special? Wouldn't also other kinds of > methods and class constructors benefit from such a feature? Take for > example the interface SortedSet and one of it's implementations: > TreeSet. It would be desirable that constructors not taking a > Comparator restrict type variable to a subtype of Comparable. JSR 14 designed generics in a particular way, such as the bound of a type variable having certain restrictions. We do not re-open the design of a former JSR lightly. Extension methods (or rather, the library evolution that needs extension methods) may provide a strong enough reason. Alex From vincent at sevel.eu Thu Nov 18 09:09:13 2010 From: vincent at sevel.eu (Vincent Sevel) Date: Thu, 18 Nov 2010 18:09:13 +0100 Subject: capturing (or not) mutable local variables Message-ID: Hi Brian, I came to you yesterday at devoxx to ask about the rationale behind the decision to not capture mutable local variables. I understand that among several arguments, there is a strong one about preventing people from doing unsafe things. you gave the analogy of the kid, the fence and the pool. if I continue on the analogy, we could say that if the kid disguised himself (eg: final int[] sum = new int[1]) then you would be probably happy, although it did not take much to workaround your fence, and actually people are used to it. of course your fear completely disappears if the variable is of type anything as long as it is final. so you do not have problem with that: class BigGuyThatDoesnotKnowHowToSwim implements Foreachable { int sum = 0; public void do(Object elt) { sum += (Integer) elt; } } final BigGuyThatDoesnotKnowHowToSwim bigguy = new BigGuyThatDoesnotKnowHowToSwim(); mylist.foreach(bigguy); really, from the thread safety perspective, the disguised kid and the bigguy are not better than the kid. asking if the guy is a kid is actually the wrong question. you should be asking yourself whether or not the guy actually knows how to swim, assuming he needs to be able to. because you have kids that swim better than you and me, and you have grown up that do not know. this thought leads to another one. in your example you are assuming that the foreach method will get into multithreaded stuff. myself, I did not expect it to be. but if that is the case, truly we want to be careful about it. but should I assume that any method that accepts a block can get into MT? and should I design around that assumption? the basic problem is that you can't figure out what a pool is. so you are saying there are pools out there, let's protect anything that is accessible by kids. so you will protect football fields and playgrounds too. if you are going that route I could argue you could go even further. if multithread safety is the issue, and I cannot rely on the designer to document the limitations of the api, or I cannot rely on the user to use the api the designer had anticipated, we are actually at risk on any method that accepts an argument with state. on that particular issue, it is not about final variables, it is about passing mutable state that supports the contract set by the api. If you had told me that your impl was using MT stuff under the cover, then I would have written a very different block: Object sync = new Object(); int sum = 0; list.forEach(#{ e -> synchronized(synch) { sum += e.size(); }}); or if I knew there was a pool I would have written a BigGuyThatKnowsHowToSwim. in conclusion your fence is not only overprotecting in cases where we do not need it, but actually it provides protection only in a minority of cases. so really what you want to do if you are not confident that people will read the documentation and take responsibility for their mistakes, you should help them make sure they thought about the issue when we know there is certainty about it. your foreach impl coud say: public void forEach(@Multithreaded Foreachable block) {...} then I would get a warning or an error as a client if I tried that: list.forEach(#{ e -> sum += e.size(); }); but the error would disappear if I did: list.forEach(@ThreadSafe #{ e -> synchronized(synch) { sum += e.size(); }}); the good thing about this is compiler would still complain about the following, and that, in spite of having the final keyword, which is in no way a guarantee of thread safety: final int[] sum = new int[1]; list.forEach(#{ e -> sum[0] += e.size(); }); I believe that if you do multithread your foreach, you will have not more (or less for that matter) problems with or without capturing mutable variables. because people will write blocks of code that do not access local variables, but still manipulate pojos in a non thread safe way. for that matter, you should probably name your loop method forEachMT. as a final point I would say that the issue is not about mutable variables, it is about carrying clearly the contract constraints to the user of the api. if the spec stays that way, I am hoping that this is not based on the pool paradigm. in the jdk we should learn more from dynamic languages about trust and responsibility taking. regards. vincent From neal at gafter.com Fri Nov 19 16:52:50 2010 From: neal at gafter.com (Neal Gafter) Date: Fri, 19 Nov 2010 16:52:50 -0800 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: Vincent- I for one agree with you wholeheartedly. Designing a language feature around one particular category of use case generally results in a poor language feature and a need for further extensions. I've written about this phenomenon before < http://gafter.blogspot.com/2006/09/failure-of-imagination-in-language_17.html >. Cheers, Neal On Thu, Nov 18, 2010 at 9:09 AM, Vincent Sevel wrote: > Hi Brian, I came to you yesterday at devoxx to ask about the rationale > behind the decision to not capture mutable local variables. > I understand that among several arguments, there is a strong one about > preventing people from doing unsafe things. you gave the analogy of > the kid, the fence and the pool. > > if I continue on the analogy, we could say that if the kid disguised > himself (eg: final int[] sum = new int[1]) then you would be probably > happy, although it did not take much to workaround your fence, and > actually people are used to it. > > of course your fear completely disappears if the variable is of type > anything as long as it is final. so you do not have problem with that: > > class BigGuyThatDoesnotKnowHowToSwim implements Foreachable { > int sum = 0; > > public void do(Object elt) { > sum += (Integer) elt; > } > } > > final BigGuyThatDoesnotKnowHowToSwim bigguy = new > BigGuyThatDoesnotKnowHowToSwim(); > mylist.foreach(bigguy); > > really, from the thread safety perspective, the disguised kid and the > bigguy are not better than the kid. asking if the guy is a kid is > actually the wrong question. you should be asking yourself whether or > not the guy actually knows how to swim, assuming he needs to be able > to. because you have kids that swim better than you and me, and you > have grown up that do not know. > > this thought leads to another one. in your example you are assuming > that the foreach method will get into multithreaded stuff. myself, I > did not expect it to be. but if that is the case, truly we want to be > careful about it. but should I assume that any method that accepts a > block can get into MT? and should I design around that assumption? the > basic problem is that you can't figure out what a pool is. so you are > saying there are pools out there, let's protect anything that is > accessible by kids. so you will protect football fields and > playgrounds too. > > if you are going that route I could argue you could go even further. > if multithread safety is the issue, and I cannot rely on the designer > to document the limitations of the api, or I cannot rely on the user > to use the api the designer had anticipated, we are actually at risk > on any method that accepts an argument with state. > > on that particular issue, it is not about final variables, it is about > passing mutable state that supports the contract set by the api. If > you had told me that your impl was using MT stuff under the cover, > then I would have written a very different block: > > Object sync = new Object(); > int sum = 0; > list.forEach(#{ e -> synchronized(synch) { sum += e.size(); }}); > > or if I knew there was a pool I would have written a > BigGuyThatKnowsHowToSwim. > > in conclusion your fence is not only overprotecting in cases where we > do not need it, but actually it provides protection only in a minority > of cases. so really what you want to do if you are not confident that > people will read the documentation and take responsibility for their > mistakes, you should help them make sure they thought about the issue > when we know there is certainty about it. > > your foreach impl coud say: > > public void forEach(@Multithreaded Foreachable block) {...} > > then I would get a warning or an error as a client if I tried that: > > list.forEach(#{ e -> sum += e.size(); }); > > but the error would disappear if I did: > > list.forEach(@ThreadSafe #{ e -> synchronized(synch) { sum += e.size(); > }}); > > the good thing about this is compiler would still complain about the > following, and that, in spite of having the final keyword, which is in > no way a guarantee of thread safety: > > final int[] sum = new int[1]; > list.forEach(#{ e -> sum[0] += e.size(); }); > > I believe that if you do multithread your foreach, you will have not > more (or less for that matter) problems with or without capturing > mutable variables. because people will write blocks of code that do > not access local variables, but still manipulate pojos in a non thread > safe way. > > for that matter, you should probably name your loop method forEachMT. > > as a final point I would say that the issue is not about mutable > variables, it is about carrying clearly the contract constraints to > the user of the api. if the spec stays that way, I am hoping that this > is not based on the pool paradigm. in the jdk we should learn more > from dynamic languages about trust and responsibility taking. > > regards. > > vincent > > From jesse.sightler at gmail.com Fri Nov 19 17:12:08 2010 From: jesse.sightler at gmail.com (Jesse Sightler) Date: Fri, 19 Nov 2010 20:12:08 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: As a user of the Java language I completely agree that the benefits of local mutable variable access far outweigh the risks of misuse. On Nov 19, 2010 7:54 PM, "Neal Gafter" wrote: > Vincent- > > I for one agree with you wholeheartedly. Designing a language feature > around one particular category of use case generally results in a poor > language feature and a need for further extensions. I've written about this > phenomenon before < > http://gafter.blogspot.com/2006/09/failure-of-imagination-in-language_17.html >>. > > Cheers, > Neal > > On Thu, Nov 18, 2010 at 9:09 AM, Vincent Sevel wrote: > >> Hi Brian, I came to you yesterday at devoxx to ask about the rationale >> behind the decision to not capture mutable local variables. >> I understand that among several arguments, there is a strong one about >> preventing people from doing unsafe things. you gave the analogy of >> the kid, the fence and the pool. >> >> if I continue on the analogy, we could say that if the kid disguised >> himself (eg: final int[] sum = new int[1]) then you would be probably >> happy, although it did not take much to workaround your fence, and >> actually people are used to it. >> >> of course your fear completely disappears if the variable is of type >> anything as long as it is final. so you do not have problem with that: >> >> class BigGuyThatDoesnotKnowHowToSwim implements Foreachable { >> int sum = 0; >> >> public void do(Object elt) { >> sum += (Integer) elt; >> } >> } >> >> final BigGuyThatDoesnotKnowHowToSwim bigguy = new >> BigGuyThatDoesnotKnowHowToSwim(); >> mylist.foreach(bigguy); >> >> really, from the thread safety perspective, the disguised kid and the >> bigguy are not better than the kid. asking if the guy is a kid is >> actually the wrong question. you should be asking yourself whether or >> not the guy actually knows how to swim, assuming he needs to be able >> to. because you have kids that swim better than you and me, and you >> have grown up that do not know. >> >> this thought leads to another one. in your example you are assuming >> that the foreach method will get into multithreaded stuff. myself, I >> did not expect it to be. but if that is the case, truly we want to be >> careful about it. but should I assume that any method that accepts a >> block can get into MT? and should I design around that assumption? the >> basic problem is that you can't figure out what a pool is. so you are >> saying there are pools out there, let's protect anything that is >> accessible by kids. so you will protect football fields and >> playgrounds too. >> >> if you are going that route I could argue you could go even further. >> if multithread safety is the issue, and I cannot rely on the designer >> to document the limitations of the api, or I cannot rely on the user >> to use the api the designer had anticipated, we are actually at risk >> on any method that accepts an argument with state. >> >> on that particular issue, it is not about final variables, it is about >> passing mutable state that supports the contract set by the api. If >> you had told me that your impl was using MT stuff under the cover, >> then I would have written a very different block: >> >> Object sync = new Object(); >> int sum = 0; >> list.forEach(#{ e -> synchronized(synch) { sum += e.size(); }}); >> >> or if I knew there was a pool I would have written a >> BigGuyThatKnowsHowToSwim. >> >> in conclusion your fence is not only overprotecting in cases where we >> do not need it, but actually it provides protection only in a minority >> of cases. so really what you want to do if you are not confident that >> people will read the documentation and take responsibility for their >> mistakes, you should help them make sure they thought about the issue >> when we know there is certainty about it. >> >> your foreach impl coud say: >> >> public void forEach(@Multithreaded Foreachable block) {...} >> >> then I would get a warning or an error as a client if I tried that: >> >> list.forEach(#{ e -> sum += e.size(); }); >> >> but the error would disappear if I did: >> >> list.forEach(@ThreadSafe #{ e -> synchronized(synch) { sum += e.size(); >> }}); >> >> the good thing about this is compiler would still complain about the >> following, and that, in spite of having the final keyword, which is in >> no way a guarantee of thread safety: >> >> final int[] sum = new int[1]; >> list.forEach(#{ e -> sum[0] += e.size(); }); >> >> I believe that if you do multithread your foreach, you will have not >> more (or less for that matter) problems with or without capturing >> mutable variables. because people will write blocks of code that do >> not access local variables, but still manipulate pojos in a non thread >> safe way. >> >> for that matter, you should probably name your loop method forEachMT. >> >> as a final point I would say that the issue is not about mutable >> variables, it is about carrying clearly the contract constraints to >> the user of the api. if the spec stays that way, I am hoping that this >> is not based on the pool paradigm. in the jdk we should learn more >> from dynamic languages about trust and responsibility taking. >> >> regards. >> >> vincent >> >> > From brian.goetz at oracle.com Sat Nov 20 01:29:33 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 20 Nov 2010 10:29:33 +0100 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> > Hi Brian, I came to you yesterday at devoxx to ask about the rationale > behind the decision to not capture mutable local variables. I admire your passion and your persistence :) > I understand that among several arguments, there is a strong one about > preventing people from doing unsafe things. you gave the analogy of > the kid, the fence and the pool. Safety mechanisms do not need to be perfect to be useful. Your arguments seem to center around "I can climb the fence, so there's no point in having it." This argument is, to be blunt, silly. The design principles of the Java language stress safety, even if there are cases where such safety guards can be "casted away" by a sufficiently motivated user. If you want to capture a not-effectively-final local variable in a closure, then one of the following two things has to happen: a) The lifetime of the variable must be extended to the lifetime of the closure b) The lifetime of the closure must be shortened to the lifetime of the variable. In both cases, we would want to ensure the variable is only accessed from the thread capturing the closure. In (a), we would be effectively creating a whole new class of variables (in addition to the seven (or eight, depending on how you count) already defined by the JLS); these are no longer local variables and should not look like them. In both (a) and (b), we would be creating a kind of restricted closure, whose execution is restricted in space (confined to a specific thread) and/or time (confined to the lifetime of the scope in which the variable is declared.) For concreteness let's call these confined variables and confined lambdas. It is possible to create mechanisms to reify and enforce these various types of confinement (we've discussed them extensively internally.) However, they add nontrivial complexity to the language. In order to justify the complexity that these new features would generate, there needs to be a compelling use case. When I explored this issue, I asked a number of people to write down a use case for this. Every one wrote some form of: int sum = 0; list.forEach( #{ x -> sum += x.foo() } In a parellel world, this idiom is irretrievably broken. (See Guy Steele's presentation "Organizing Functional Code for Parallel Execution, or, foldl considered slightly harmful.") People will do this, they will do this without thinking, and their code will be broken. It is very hard, even for experts, to get this right. While Java has plenty of other opportunities to create non-thread-safe code, I am not going to create an entirely new and nearly irresistible vector for doing so. Iteration and side-effects are how we've been trained to do things in Java, but we have to learn to do better. In sum, adding this feature that you (and others) want so badly seems to add up to: - Lots of new complexity (e.g., confined variables and lambdas) - Nearly irresistible new areas for making errors - All to prop up a broken programming model (iterations + side effects) To quote a past president: "Wouldn't be prudent." At this point in time, it feels the risk and complexity outweighs the benefit. I would rather put the effort into supporting map/reduce-y idioms in the libraries. The above block is much better as: list.reduce( #{ x, y => x+y } ) or list.reduce(Reducers.SUM) or list.sum() > this thought leads to another one. in your example you are assuming > that the foreach method will get into multithreaded stuff. Not quite: s/will/may/ > your foreach impl coud say: > > public void forEach(@Multithreaded Foreachable block) {...} We explored these issues in the JSR-166 expert group several years ago. The basic problem is that we don't have a way of reliably expressing "reference to thread-safe object" in the type system (either statically or dynamically), so these things revert to being documentation rather than type assertions. > for that matter, you should probably name your loop method forEachMT. Under consideration. Though my intuition is that (a) such a convention will be hard to stick to and (b) in five years it will probably look silly. From mthornton at optrak.co.uk Sat Nov 20 01:34:39 2010 From: mthornton at optrak.co.uk (Mark Thornton) Date: Sat, 20 Nov 2010 09:34:39 +0000 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: <4CE7962F.1090201@optrak.co.uk> On 20/11/2010 01:12, Jesse Sightler wrote: > As a user of the Java language I completely agree that the benefits of local > mutable variable access far outweigh the risks of misuse. It is hard to assess those risks of misuse when the intent of the change is to modify practice. We are to expect many more instances of API with multithreaded implementations, and thus perhaps many more opportunities to make mistakes if no barriers to misuse are implemented. It would be nice if local mutable variable access was possible, but using it a (potentially) multithreaded context generated an error or warning. Mark From grev at miginfocom.com Sat Nov 20 02:37:49 2010 From: grev at miginfocom.com (Mikael Grev) Date: Sat, 20 Nov 2010 11:37:49 +0100 Subject: capturing (or not) mutable local variables In-Reply-To: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> Message-ID: Please don't make it possible to mutate the variable from within the closure. However, I wouldn't mind if "effectively-final" was extended so that either: 1) The closure works on a copy, giving no restrictions on the initialization and reassignment of the captured variable 2) Any variable is effectively-final as long as it isn't reassigned after the closure initialization. (reordering should be solvable) The reason for this is that the following isn't that uncommon and "i" needs to be reassigned as another variable to be effectively-final (ugly): int i = 0; if (something) i = xxx; capture of i Loosening the definition of effectively-final would effectively make the feature easier to use. Cheers, Mikael On Nov 20, 2010, at 10:29 AM, Brian Goetz wrote: >> Hi Brian, I came to you yesterday at devoxx to ask about the rationale >> behind the decision to not capture mutable local variables. > > I admire your passion and your persistence :) > >> I understand that among several arguments, there is a strong one about >> preventing people from doing unsafe things. you gave the analogy of >> the kid, the fence and the pool. > > Safety mechanisms do not need to be perfect to be useful. Your arguments seem to center around "I can climb the fence, so there's no point in having it." This argument is, to be blunt, silly. The design principles of the Java language stress safety, even if there are cases where such safety guards can be "casted away" by a sufficiently motivated user. > > If you want to capture a not-effectively-final local variable in a closure, then one of the following two things has to happen: > a) The lifetime of the variable must be extended to the lifetime of the closure > b) The lifetime of the closure must be shortened to the lifetime of the variable. > > In both cases, we would want to ensure the variable is only accessed from the thread capturing the closure. In (a), we would be effectively creating a whole new class of variables (in addition to the seven (or eight, depending on how you count) already defined by the JLS); these are no longer local variables and should not look like them. In both (a) and (b), we would be creating a kind of restricted closure, whose execution is restricted in space (confined to a specific thread) and/or time (confined to the lifetime of the scope in which the variable is declared.) For concreteness let's call these confined variables and confined lambdas. > > It is possible to create mechanisms to reify and enforce these various types of confinement (we've discussed them extensively internally.) However, they add nontrivial complexity to the language. > > In order to justify the complexity that these new features would generate, there needs to be a compelling use case. When I explored this issue, I asked a number of people to write down a use case for this. Every one wrote some form of: > > int sum = 0; > list.forEach( #{ x -> sum += x.foo() } > > In a parellel world, this idiom is irretrievably broken. (See Guy Steele's presentation "Organizing Functional Code for Parallel Execution, or, foldl considered slightly harmful.") People will do this, they will do this without thinking, and their code will be broken. It is very hard, even for experts, to get this right. While Java has plenty of other opportunities to create non-thread-safe code, I am not going to create an entirely new and nearly irresistible vector for doing so. Iteration and side-effects are how we've been trained to do things in Java, but we have to learn to do better. > > In sum, adding this feature that you (and others) want so badly seems to add up to: > - Lots of new complexity (e.g., confined variables and lambdas) > - Nearly irresistible new areas for making errors > - All to prop up a broken programming model (iterations + side effects) > > To quote a past president: "Wouldn't be prudent." At this point in time, it feels the risk and complexity outweighs the benefit. I would rather put the effort into supporting map/reduce-y idioms in the libraries. The above block is much better as: > > list.reduce( #{ x, y => x+y } ) > or > list.reduce(Reducers.SUM) > or > list.sum() > >> this thought leads to another one. in your example you are assuming >> that the foreach method will get into multithreaded stuff. > > Not quite: s/will/may/ > >> your foreach impl coud say: >> >> public void forEach(@Multithreaded Foreachable block) {...} > > We explored these issues in the JSR-166 expert group several years ago. The basic problem is that we don't have a way of reliably expressing "reference to thread-safe object" in the type system (either statically or dynamically), so these things revert to being documentation rather than type assertions. > >> for that matter, you should probably name your loop method forEachMT. > > Under consideration. Though my intuition is that (a) such a convention will be hard to stick to and (b) in five years it will probably look silly. From markmahieu at gmail.com Sat Nov 20 03:43:14 2010 From: markmahieu at gmail.com (Mark Mahieu) Date: Sat, 20 Nov 2010 11:43:14 +0000 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> Message-ID: <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> I have a bit of a soft spot for this idea, in fact I think I posted something similar a few months ago. It's quite appealing for certain examples, but I do wonder whether it would mainly just contribute to number of rules a programmer has to deal with in this area of the language. I think I concluded that this kind of 'loosening' would work better if it were more explicit, perhaps still requiring the programmer to mark the variable final, but by allowing the same variable to be 'redeclared' final rather than having to invent a new one (that's the ugly bit). I might be wandering off into the weeds now though... Mark On 20 Nov 2010, at 10:37, Mikael Grev wrote: > Please don't make it possible to mutate the variable from within the closure. > > > However, I wouldn't mind if "effectively-final" was extended so that either: > > 1) The closure works on a copy, giving no restrictions on the initialization and reassignment of the captured variable > 2) Any variable is effectively-final as long as it isn't reassigned after the closure initialization. (reordering should be solvable) > > The reason for this is that the following isn't that uncommon and "i" needs to be reassigned as another variable to be effectively-final (ugly): > > int i = 0; > if (something) > i = xxx; > > capture of i > > Loosening the definition of effectively-final would effectively make the feature easier to use. > > Cheers, > Mikael > > > On Nov 20, 2010, at 10:29 AM, Brian Goetz wrote: > >>> Hi Brian, I came to you yesterday at devoxx to ask about the rationale >>> behind the decision to not capture mutable local variables. >> >> I admire your passion and your persistence :) >> >>> I understand that among several arguments, there is a strong one about >>> preventing people from doing unsafe things. you gave the analogy of >>> the kid, the fence and the pool. >> >> Safety mechanisms do not need to be perfect to be useful. Your arguments seem to center around "I can climb the fence, so there's no point in having it." This argument is, to be blunt, silly. The design principles of the Java language stress safety, even if there are cases where such safety guards can be "casted away" by a sufficiently motivated user. >> >> If you want to capture a not-effectively-final local variable in a closure, then one of the following two things has to happen: >> a) The lifetime of the variable must be extended to the lifetime of the closure >> b) The lifetime of the closure must be shortened to the lifetime of the variable. >> >> In both cases, we would want to ensure the variable is only accessed from the thread capturing the closure. In (a), we would be effectively creating a whole new class of variables (in addition to the seven (or eight, depending on how you count) already defined by the JLS); these are no longer local variables and should not look like them. In both (a) and (b), we would be creating a kind of restricted closure, whose execution is restricted in space (confined to a specific thread) and/or time (confined to the lifetime of the scope in which the variable is declared.) For concreteness let's call these confined variables and confined lambdas. >> >> It is possible to create mechanisms to reify and enforce these various types of confinement (we've discussed them extensively internally.) However, they add nontrivial complexity to the language. >> >> In order to justify the complexity that these new features would generate, there needs to be a compelling use case. When I explored this issue, I asked a number of people to write down a use case for this. Every one wrote some form of: >> >> int sum = 0; >> list.forEach( #{ x -> sum += x.foo() } >> >> In a parellel world, this idiom is irretrievably broken. (See Guy Steele's presentation "Organizing Functional Code for Parallel Execution, or, foldl considered slightly harmful.") People will do this, they will do this without thinking, and their code will be broken. It is very hard, even for experts, to get this right. While Java has plenty of other opportunities to create non-thread-safe code, I am not going to create an entirely new and nearly irresistible vector for doing so. Iteration and side-effects are how we've been trained to do things in Java, but we have to learn to do better. >> >> In sum, adding this feature that you (and others) want so badly seems to add up to: >> - Lots of new complexity (e.g., confined variables and lambdas) >> - Nearly irresistible new areas for making errors >> - All to prop up a broken programming model (iterations + side effects) >> >> To quote a past president: "Wouldn't be prudent." At this point in time, it feels the risk and complexity outweighs the benefit. I would rather put the effort into supporting map/reduce-y idioms in the libraries. The above block is much better as: >> >> list.reduce( #{ x, y => x+y } ) >> or >> list.reduce(Reducers.SUM) >> or >> list.sum() >> >>> this thought leads to another one. in your example you are assuming >>> that the foreach method will get into multithreaded stuff. >> >> Not quite: s/will/may/ >> >>> your foreach impl coud say: >>> >>> public void forEach(@Multithreaded Foreachable block) {...} >> >> We explored these issues in the JSR-166 expert group several years ago. The basic problem is that we don't have a way of reliably expressing "reference to thread-safe object" in the type system (either statically or dynamically), so these things revert to being documentation rather than type assertions. >> >>> for that matter, you should probably name your loop method forEachMT. >> >> Under consideration. Though my intuition is that (a) such a convention will be hard to stick to and (b) in five years it will probably look silly. > > From collin.fagan at gmail.com Sat Nov 20 06:05:58 2010 From: collin.fagan at gmail.com (Collin Fagan) Date: Sat, 20 Nov 2010 08:05:58 -0600 Subject: capturing (or not) mutable local variables In-Reply-To: <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> Message-ID: I think I'm hearing that mutating a local variable from a closure is just a bad idea in general right? And the pattern of using a single element array or an AtomicReference wrapper is dangerous and should be discouraged? I personally have found the lack of a "StrongReference" implementation disappointing and something like that might be helpful here but if this pattern is bad and bad,bad,bad, then I'm not even going to go there. Collin On Sat, Nov 20, 2010 at 5:43 AM, Mark Mahieu wrote: > I have a bit of a soft spot for this idea, in fact I think I posted > something similar a few months ago. > > It's quite appealing for certain examples, but I do wonder whether it would > mainly just contribute to number of rules a programmer has to deal with in > this area of the language. > > I think I concluded that this kind of 'loosening' would work better if it > were more explicit, perhaps still requiring the programmer to mark the > variable final, but by allowing the same variable to be 'redeclared' final > rather than having to invent a new one (that's the ugly bit). I might be > wandering off into the weeds now though... > > Mark > > > On 20 Nov 2010, at 10:37, Mikael Grev wrote: > > > Please don't make it possible to mutate the variable from within the > closure. > > > > > > However, I wouldn't mind if "effectively-final" was extended so that > either: > > > > 1) The closure works on a copy, giving no restrictions on the > initialization and reassignment of the captured variable > > 2) Any variable is effectively-final as long as it isn't reassigned after > the closure initialization. (reordering should be solvable) > > > > The reason for this is that the following isn't that uncommon and "i" > needs to be reassigned as another variable to be effectively-final (ugly): > > > > int i = 0; > > if (something) > > i = xxx; > > > > capture of i > > > > Loosening the definition of effectively-final would effectively make the > feature easier to use. > > > > Cheers, > > Mikael > > > > > > On Nov 20, 2010, at 10:29 AM, Brian Goetz wrote: > > > >>> Hi Brian, I came to you yesterday at devoxx to ask about the rationale > >>> behind the decision to not capture mutable local variables. > >> > >> I admire your passion and your persistence :) > >> > >>> I understand that among several arguments, there is a strong one about > >>> preventing people from doing unsafe things. you gave the analogy of > >>> the kid, the fence and the pool. > >> > >> Safety mechanisms do not need to be perfect to be useful. Your > arguments seem to center around "I can climb the fence, so there's no point > in having it." This argument is, to be blunt, silly. The design principles > of the Java language stress safety, even if there are cases where such > safety guards can be "casted away" by a sufficiently motivated user. > >> > >> If you want to capture a not-effectively-final local variable in a > closure, then one of the following two things has to happen: > >> a) The lifetime of the variable must be extended to the lifetime of the > closure > >> b) The lifetime of the closure must be shortened to the lifetime of the > variable. > >> > >> In both cases, we would want to ensure the variable is only accessed > from the thread capturing the closure. In (a), we would be effectively > creating a whole new class of variables (in addition to the seven (or eight, > depending on how you count) already defined by the JLS); these are no longer > local variables and should not look like them. In both (a) and (b), we > would be creating a kind of restricted closure, whose execution is > restricted in space (confined to a specific thread) and/or time (confined to > the lifetime of the scope in which the variable is declared.) For > concreteness let's call these confined variables and confined lambdas. > >> > >> It is possible to create mechanisms to reify and enforce these various > types of confinement (we've discussed them extensively internally.) > However, they add nontrivial complexity to the language. > >> > >> In order to justify the complexity that these new features would > generate, there needs to be a compelling use case. When I explored this > issue, I asked a number of people to write down a use case for this. Every > one wrote some form of: > >> > >> int sum = 0; > >> list.forEach( #{ x -> sum += x.foo() } > >> > >> In a parellel world, this idiom is irretrievably broken. (See Guy > Steele's presentation "Organizing Functional Code for Parallel Execution, > or, foldl considered slightly harmful.") People will do this, they will do > this without thinking, and their code will be broken. It is very hard, even > for experts, to get this right. While Java has plenty of other > opportunities to create non-thread-safe code, I am not going to create an > entirely new and nearly irresistible vector for doing so. Iteration and > side-effects are how we've been trained to do things in Java, but we have to > learn to do better. > >> > >> In sum, adding this feature that you (and others) want so badly seems to > add up to: > >> - Lots of new complexity (e.g., confined variables and lambdas) > >> - Nearly irresistible new areas for making errors > >> - All to prop up a broken programming model (iterations + side effects) > >> > >> To quote a past president: "Wouldn't be prudent." At this point in > time, it feels the risk and complexity outweighs the benefit. I would > rather put the effort into supporting map/reduce-y idioms in the libraries. > The above block is much better as: > >> > >> list.reduce( #{ x, y => x+y } ) > >> or > >> list.reduce(Reducers.SUM) > >> or > >> list.sum() > >> > >>> this thought leads to another one. in your example you are assuming > >>> that the foreach method will get into multithreaded stuff. > >> > >> Not quite: s/will/may/ > >> > >>> your foreach impl coud say: > >>> > >>> public void forEach(@Multithreaded Foreachable block) {...} > >> > >> We explored these issues in the JSR-166 expert group several years ago. > The basic problem is that we don't have a way of reliably expressing > "reference to thread-safe object" in the type system (either statically or > dynamically), so these things revert to being documentation rather than type > assertions. > >> > >>> for that matter, you should probably name your loop method forEachMT. > >> > >> Under consideration. Though my intuition is that (a) such a convention > will be hard to stick to and (b) in five years it will probably look silly. > > > > > > > From isidore at setgame.com Sat Nov 20 07:21:33 2010 From: isidore at setgame.com (Llewellyn Falco) Date: Sat, 20 Nov 2010 07:21:33 -0800 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> Message-ID: Ok, again we can look to other languages to see how this ends up working in practice. There are 3 cases I can easily think of. 1) multi-threading. If things are mutable this almost always can go wrong in theory. In practice it only sometimes goes wrong :-) but it's not consistent 2) reading a variable - single threaded This almost always goes right. And is extremely useful. The case where this goes wrong is when the variable changes before the execution of the lambda. The classic example of this from c# is for (int i =0;i <10;i++) lambdas.add(()=>i); foreach(var l in lambdas) Print(l()); Doesn't print 0..9 as excepted, but 10 10's This case can get you, but is fairly infrequent in single threads. 3) reassigning a local variable - single threaded This is also extremely useful and acts as expected. Namely the last assignment is what sticks. An example commonly used from testing is int i = 0; SomeMethod(()=> i++) assertEquals("lamba was called 6 times",6,i); All that being said, I believe accessing local variable is a very important part of lambdas. As I have mentioned I use lambdas daily both in c# and java 6 ( via my lambda jar). I am constantly using the wrapper technique to handle access. However, I don't think you should add the change. Not because it isn't thread safe, (if you want to use that argument let's talk about removing non final aspects of fields as well). But because I think lambdas should compile identically to the anonymous classes that you can already create for SAMs. And currently they can't access non final locals. Lambdas are important. I personally believe the delay of them is responsible for a large portion of the brain drain we have seen leaving java over the last few years. Let's get them fast. And not changing the scoping rules seems like a good idea to achieving a smaller, faster step. Llewellyn Falco http://bit.ly/lambdas Sent from my iPad On Nov 20, 2010, at 6:05 AM, Collin Fagan wrote: > I think I'm hearing that mutating a local variable from a closure is just a > bad idea in general right? And the pattern of using a single element array > or an AtomicReference wrapper is dangerous and should be discouraged? I > personally have found the lack of a "StrongReference" implementation > disappointing and something like that might be helpful here but if this > pattern is bad and bad,bad,bad, then I'm not even going to go there. > > Collin > > On Sat, Nov 20, 2010 at 5:43 AM, Mark Mahieu wrote: > >> I have a bit of a soft spot for this idea, in fact I think I posted >> something similar a few months ago. >> >> It's quite appealing for certain examples, but I do wonder whether it would >> mainly just contribute to number of rules a programmer has to deal with in >> this area of the language. >> >> I think I concluded that this kind of 'loosening' would work better if it >> were more explicit, perhaps still requiring the programmer to mark the >> variable final, but by allowing the same variable to be 'redeclared' final >> rather than having to invent a new one (that's the ugly bit). I might be >> wandering off into the weeds now though... >> >> Mark >> >> >> On 20 Nov 2010, at 10:37, Mikael Grev wrote: >> >>> Please don't make it possible to mutate the variable from within the >> closure. >>> >>> >>> However, I wouldn't mind if "effectively-final" was extended so that >> either: >>> >>> 1) The closure works on a copy, giving no restrictions on the >> initialization and reassignment of the captured variable >>> 2) Any variable is effectively-final as long as it isn't reassigned after >> the closure initialization. (reordering should be solvable) >>> >>> The reason for this is that the following isn't that uncommon and "i" >> needs to be reassigned as another variable to be effectively-final (ugly): >>> >>> int i = 0; >>> if (something) >>> i = xxx; >>> >>> capture of i >>> >>> Loosening the definition of effectively-final would effectively make the >> feature easier to use. >>> >>> Cheers, >>> Mikael >>> >>> >>> On Nov 20, 2010, at 10:29 AM, Brian Goetz wrote: >>> >>>>> Hi Brian, I came to you yesterday at devoxx to ask about the rationale >>>>> behind the decision to not capture mutable local variables. >>>> >>>> I admire your passion and your persistence :) >>>> >>>>> I understand that among several arguments, there is a strong one about >>>>> preventing people from doing unsafe things. you gave the analogy of >>>>> the kid, the fence and the pool. >>>> >>>> Safety mechanisms do not need to be perfect to be useful. Your >> arguments seem to center around "I can climb the fence, so there's no point >> in having it." This argument is, to be blunt, silly. The design principles >> of the Java language stress safety, even if there are cases where such >> safety guards can be "casted away" by a sufficiently motivated user. >>>> >>>> If you want to capture a not-effectively-final local variable in a >> closure, then one of the following two things has to happen: >>>> a) The lifetime of the variable must be extended to the lifetime of the >> closure >>>> b) The lifetime of the closure must be shortened to the lifetime of the >> variable. >>>> >>>> In both cases, we would want to ensure the variable is only accessed >> from the thread capturing the closure. In (a), we would be effectively >> creating a whole new class of variables (in addition to the seven (or eight, >> depending on how you count) already defined by the JLS); these are no longer >> local variables and should not look like them. In both (a) and (b), we >> would be creating a kind of restricted closure, whose execution is >> restricted in space (confined to a specific thread) and/or time (confined to >> the lifetime of the scope in which the variable is declared.) For >> concreteness let's call these confined variables and confined lambdas. >>>> >>>> It is possible to create mechanisms to reify and enforce these various >> types of confinement (we've discussed them extensively internally.) >> However, they add nontrivial complexity to the language. >>>> >>>> In order to justify the complexity that these new features would >> generate, there needs to be a compelling use case. When I explored this >> issue, I asked a number of people to write down a use case for this. Every >> one wrote some form of: >>>> >>>> int sum = 0; >>>> list.forEach( #{ x -> sum += x.foo() } >>>> >>>> In a parellel world, this idiom is irretrievably broken. (See Guy >> Steele's presentation "Organizing Functional Code for Parallel Execution, >> or, foldl considered slightly harmful.") People will do this, they will do >> this without thinking, and their code will be broken. It is very hard, even >> for experts, to get this right. While Java has plenty of other >> opportunities to create non-thread-safe code, I am not going to create an >> entirely new and nearly irresistible vector for doing so. Iteration and >> side-effects are how we've been trained to do things in Java, but we have to >> learn to do better. >>>> >>>> In sum, adding this feature that you (and others) want so badly seems to >> add up to: >>>> - Lots of new complexity (e.g., confined variables and lambdas) >>>> - Nearly irresistible new areas for making errors >>>> - All to prop up a broken programming model (iterations + side effects) >>>> >>>> To quote a past president: "Wouldn't be prudent." At this point in >> time, it feels the risk and complexity outweighs the benefit. I would >> rather put the effort into supporting map/reduce-y idioms in the libraries. >> The above block is much better as: >>>> >>>> list.reduce( #{ x, y => x+y } ) >>>> or >>>> list.reduce(Reducers.SUM) >>>> or >>>> list.sum() >>>> >>>>> this thought leads to another one. in your example you are assuming >>>>> that the foreach method will get into multithreaded stuff. >>>> >>>> Not quite: s/will/may/ >>>> >>>>> your foreach impl coud say: >>>>> >>>>> public void forEach(@Multithreaded Foreachable block) {...} >>>> >>>> We explored these issues in the JSR-166 expert group several years ago. >> The basic problem is that we don't have a way of reliably expressing >> "reference to thread-safe object" in the type system (either statically or >> dynamically), so these things revert to being documentation rather than type >> assertions. >>>> >>>>> for that matter, you should probably name your loop method forEachMT. >>>> >>>> Under consideration. Though my intuition is that (a) such a convention >> will be hard to stick to and (b) in five years it will probably look silly. >>> >>> >> >> >> > From grev at miginfocom.com Sat Nov 20 07:21:05 2010 From: grev at miginfocom.com (Mikael Grev) Date: Sat, 20 Nov 2010 16:21:05 +0100 Subject: capturing (or not) mutable local variables In-Reply-To: <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> Message-ID: <0232986C-E9B4-497E-A8E9-9F7327B307B5@miginfocom.com> I'm only concerned with the usability aspect (as usual) and it is actually simpler for the users the way I suggest. The only thing the users have to concern themselves with is that changes to captured variables won't be visible outside the closure (this is actually same for the current effectually-final as well). With this loosening of effectively-final this is the only thing the user has to learn. With the current spec the user also has to understand the concept of effectively-final as well as a work around for my code below, which will always be a hack (a new final variable or an array-of-one). What differs between 1) and 2) below is whether the user can change the variable after the closure initialization or not. Therefore I would recommend 1) as that is the simplest concept, especially since Java is pass-by-value for methods already. So, basically we move complexity from the user (millions) to the compiler write (a handful) which is a good move in my book. Effectively-final the way it is done now has a smell of hack to it and won't look good i a few years, much as overprotective language concepts of yesteryear don't look good today. Cheers, Mikael On Nov 20, 2010, at 12:43 PM, Mark Mahieu wrote: > I have a bit of a soft spot for this idea, in fact I think I posted something similar a few months ago. > > It's quite appealing for certain examples, but I do wonder whether it would mainly just contribute to number of rules a programmer has to deal with in this area of the language. > > I think I concluded that this kind of 'loosening' would work better if it were more explicit, perhaps still requiring the programmer to mark the variable final, but by allowing the same variable to be 'redeclared' final rather than having to invent a new one (that's the ugly bit). I might be wandering off into the weeds now though... > > Mark > > > On 20 Nov 2010, at 10:37, Mikael Grev wrote: > >> Please don't make it possible to mutate the variable from within the closure. >> >> >> However, I wouldn't mind if "effectively-final" was extended so that either: >> >> 1) The closure works on a copy, giving no restrictions on the initialization and reassignment of the captured variable >> 2) Any variable is effectively-final as long as it isn't reassigned after the closure initialization. (reordering should be solvable) >> >> The reason for this is that the following isn't that uncommon and "i" needs to be reassigned as another variable to be effectively-final (ugly): >> >> int i = 0; >> if (something) >> i = xxx; >> >> capture of i >> >> Loosening the definition of effectively-final would effectively make the feature easier to use. >> >> Cheers, >> Mikael >> >> >> On Nov 20, 2010, at 10:29 AM, Brian Goetz wrote: >> >>>> Hi Brian, I came to you yesterday at devoxx to ask about the rationale >>>> behind the decision to not capture mutable local variables. >>> >>> I admire your passion and your persistence :) >>> >>>> I understand that among several arguments, there is a strong one about >>>> preventing people from doing unsafe things. you gave the analogy of >>>> the kid, the fence and the pool. >>> >>> Safety mechanisms do not need to be perfect to be useful. Your arguments seem to center around "I can climb the fence, so there's no point in having it." This argument is, to be blunt, silly. The design principles of the Java language stress safety, even if there are cases where such safety guards can be "casted away" by a sufficiently motivated user. >>> >>> If you want to capture a not-effectively-final local variable in a closure, then one of the following two things has to happen: >>> a) The lifetime of the variable must be extended to the lifetime of the closure >>> b) The lifetime of the closure must be shortened to the lifetime of the variable. >>> >>> In both cases, we would want to ensure the variable is only accessed from the thread capturing the closure. In (a), we would be effectively creating a whole new class of variables (in addition to the seven (or eight, depending on how you count) already defined by the JLS); these are no longer local variables and should not look like them. In both (a) and (b), we would be creating a kind of restricted closure, whose execution is restricted in space (confined to a specific thread) and/or time (confined to the lifetime of the scope in which the variable is declared.) For concreteness let's call these confined variables and confined lambdas. >>> >>> It is possible to create mechanisms to reify and enforce these various types of confinement (we've discussed them extensively internally.) However, they add nontrivial complexity to the language. >>> >>> In order to justify the complexity that these new features would generate, there needs to be a compelling use case. When I explored this issue, I asked a number of people to write down a use case for this. Every one wrote some form of: >>> >>> int sum = 0; >>> list.forEach( #{ x -> sum += x.foo() } >>> >>> In a parellel world, this idiom is irretrievably broken. (See Guy Steele's presentation "Organizing Functional Code for Parallel Execution, or, foldl considered slightly harmful.") People will do this, they will do this without thinking, and their code will be broken. It is very hard, even for experts, to get this right. While Java has plenty of other opportunities to create non-thread-safe code, I am not going to create an entirely new and nearly irresistible vector for doing so. Iteration and side-effects are how we've been trained to do things in Java, but we have to learn to do better. >>> >>> In sum, adding this feature that you (and others) want so badly seems to add up to: >>> - Lots of new complexity (e.g., confined variables and lambdas) >>> - Nearly irresistible new areas for making errors >>> - All to prop up a broken programming model (iterations + side effects) >>> >>> To quote a past president: "Wouldn't be prudent." At this point in time, it feels the risk and complexity outweighs the benefit. I would rather put the effort into supporting map/reduce-y idioms in the libraries. The above block is much better as: >>> >>> list.reduce( #{ x, y => x+y } ) >>> or >>> list.reduce(Reducers.SUM) >>> or >>> list.sum() >>> >>>> this thought leads to another one. in your example you are assuming >>>> that the foreach method will get into multithreaded stuff. >>> >>> Not quite: s/will/may/ >>> >>>> your foreach impl coud say: >>>> >>>> public void forEach(@Multithreaded Foreachable block) {...} >>> >>> We explored these issues in the JSR-166 expert group several years ago. The basic problem is that we don't have a way of reliably expressing "reference to thread-safe object" in the type system (either statically or dynamically), so these things revert to being documentation rather than type assertions. >>> >>>> for that matter, you should probably name your loop method forEachMT. >>> >>> Under consideration. Though my intuition is that (a) such a convention will be hard to stick to and (b) in five years it will probably look silly. >> >> From markmahieu at gmail.com Sat Nov 20 07:46:23 2010 From: markmahieu at gmail.com (Mark Mahieu) Date: Sat, 20 Nov 2010 15:46:23 +0000 Subject: capturing (or not) mutable local variables In-Reply-To: <0232986C-E9B4-497E-A8E9-9F7327B307B5@miginfocom.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> <0232986C-E9B4-497E-A8E9-9F7327B307B5@miginfocom.com> Message-ID: <9A68B1B9-06B0-4C2F-9CDA-A55D9B53978E@gmail.com> Ah, I must have skipped over option (1) somehow when I read your first email; my reply concerned option (2) only, sorry for any confusion. Personally, I think effectively-final is an improvement, but could be nudged along a little further. John Rose posted one interesting variation 2 or 3 weeks ago, IIRC. Mark On 20 Nov 2010, at 15:21, Mikael Grev wrote: > I'm only concerned with the usability aspect (as usual) and it is actually simpler for the users the way I suggest. > > The only thing the users have to concern themselves with is that changes to captured variables won't be visible outside the closure (this is actually same for the current effectually-final as well). > > With this loosening of effectively-final this is the only thing the user has to learn. With the current spec the user also has to understand the concept of effectively-final as well as a work around for my code below, which will always be a hack (a new final variable or an array-of-one). > > What differs between 1) and 2) below is whether the user can change the variable after the closure initialization or not. Therefore I would recommend 1) as that is the simplest concept, especially since Java is pass-by-value for methods already. > > So, basically we move complexity from the user (millions) to the compiler write (a handful) which is a good move in my book. > > Effectively-final the way it is done now has a smell of hack to it and won't look good i a few years, much as overprotective language concepts of yesteryear don't look good today. > > Cheers, > Mikael > > On Nov 20, 2010, at 12:43 PM, Mark Mahieu wrote: > >> I have a bit of a soft spot for this idea, in fact I think I posted something similar a few months ago. >> >> It's quite appealing for certain examples, but I do wonder whether it would mainly just contribute to number of rules a programmer has to deal with in this area of the language. >> >> I think I concluded that this kind of 'loosening' would work better if it were more explicit, perhaps still requiring the programmer to mark the variable final, but by allowing the same variable to be 'redeclared' final rather than having to invent a new one (that's the ugly bit). I might be wandering off into the weeds now though... >> >> Mark >> >> >> On 20 Nov 2010, at 10:37, Mikael Grev wrote: >> >>> Please don't make it possible to mutate the variable from within the closure. >>> >>> >>> However, I wouldn't mind if "effectively-final" was extended so that either: >>> >>> 1) The closure works on a copy, giving no restrictions on the initialization and reassignment of the captured variable >>> 2) Any variable is effectively-final as long as it isn't reassigned after the closure initialization. (reordering should be solvable) >>> >>> The reason for this is that the following isn't that uncommon and "i" needs to be reassigned as another variable to be effectively-final (ugly): >>> >>> int i = 0; >>> if (something) >>> i = xxx; >>> >>> capture of i >>> >>> Loosening the definition of effectively-final would effectively make the feature easier to use. >>> >>> Cheers, >>> Mikael >>> >>> >>> On Nov 20, 2010, at 10:29 AM, Brian Goetz wrote: >>> >>>>> Hi Brian, I came to you yesterday at devoxx to ask about the rationale >>>>> behind the decision to not capture mutable local variables. >>>> >>>> I admire your passion and your persistence :) >>>> >>>>> I understand that among several arguments, there is a strong one about >>>>> preventing people from doing unsafe things. you gave the analogy of >>>>> the kid, the fence and the pool. >>>> >>>> Safety mechanisms do not need to be perfect to be useful. Your arguments seem to center around "I can climb the fence, so there's no point in having it." This argument is, to be blunt, silly. The design principles of the Java language stress safety, even if there are cases where such safety guards can be "casted away" by a sufficiently motivated user. >>>> >>>> If you want to capture a not-effectively-final local variable in a closure, then one of the following two things has to happen: >>>> a) The lifetime of the variable must be extended to the lifetime of the closure >>>> b) The lifetime of the closure must be shortened to the lifetime of the variable. >>>> >>>> In both cases, we would want to ensure the variable is only accessed from the thread capturing the closure. In (a), we would be effectively creating a whole new class of variables (in addition to the seven (or eight, depending on how you count) already defined by the JLS); these are no longer local variables and should not look like them. In both (a) and (b), we would be creating a kind of restricted closure, whose execution is restricted in space (confined to a specific thread) and/or time (confined to the lifetime of the scope in which the variable is declared.) For concreteness let's call these confined variables and confined lambdas. >>>> >>>> It is possible to create mechanisms to reify and enforce these various types of confinement (we've discussed them extensively internally.) However, they add nontrivial complexity to the language. >>>> >>>> In order to justify the complexity that these new features would generate, there needs to be a compelling use case. When I explored this issue, I asked a number of people to write down a use case for this. Every one wrote some form of: >>>> >>>> int sum = 0; >>>> list.forEach( #{ x -> sum += x.foo() } >>>> >>>> In a parellel world, this idiom is irretrievably broken. (See Guy Steele's presentation "Organizing Functional Code for Parallel Execution, or, foldl considered slightly harmful.") People will do this, they will do this without thinking, and their code will be broken. It is very hard, even for experts, to get this right. While Java has plenty of other opportunities to create non-thread-safe code, I am not going to create an entirely new and nearly irresistible vector for doing so. Iteration and side-effects are how we've been trained to do things in Java, but we have to learn to do better. >>>> >>>> In sum, adding this feature that you (and others) want so badly seems to add up to: >>>> - Lots of new complexity (e.g., confined variables and lambdas) >>>> - Nearly irresistible new areas for making errors >>>> - All to prop up a broken programming model (iterations + side effects) >>>> >>>> To quote a past president: "Wouldn't be prudent." At this point in time, it feels the risk and complexity outweighs the benefit. I would rather put the effort into supporting map/reduce-y idioms in the libraries. The above block is much better as: >>>> >>>> list.reduce( #{ x, y => x+y } ) >>>> or >>>> list.reduce(Reducers.SUM) >>>> or >>>> list.sum() >>>> >>>>> this thought leads to another one. in your example you are assuming >>>>> that the foreach method will get into multithreaded stuff. >>>> >>>> Not quite: s/will/may/ >>>> >>>>> your foreach impl coud say: >>>>> >>>>> public void forEach(@Multithreaded Foreachable block) {...} >>>> >>>> We explored these issues in the JSR-166 expert group several years ago. The basic problem is that we don't have a way of reliably expressing "reference to thread-safe object" in the type system (either statically or dynamically), so these things revert to being documentation rather than type assertions. >>>> >>>>> for that matter, you should probably name your loop method forEachMT. >>>> >>>> Under consideration. Though my intuition is that (a) such a convention will be hard to stick to and (b) in five years it will probably look silly. >>> >>> > > From brian.goetz at oracle.com Sat Nov 20 12:29:32 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 20 Nov 2010 15:29:32 -0500 Subject: Announcing JSR-335: Lambda expressions in the Java Language Message-ID: <90029FF8-81D7-4D41-B444-C370610E83BA@oracle.com> I am pleased to announce that Project Lambda has moved to its next phase: we have filed a JSR (JSR-335) for Project Lambda, to be included in the Java SE 8 umbrella JSR. Pending approval by the EC, the Expert Group (currently in formation) will take as its starting point the State of the Lambda documents we have published here, validate the design, explore the dark corners, and proceed towards production of the key artifacts for a JSR -- specification, RI, and TCK. They will be joined in the effort by key contributors from Oracle, including Alex Buckley, Maurizio Cimadamore, Fredrik Ohrstrom, and Robert Field. Oracle is committed to developing Java in the open. This JSR will use a publicly readable mailing list for EG discussion, as well as a public discussion list (which may end up being this list.) As we have done in several previous JSRs, it is likely that mail from the EG list will automatically be forwarded to the public discussion list. The existing OpenJDK repository will be used for developing the RI. We'll make additional details available as we have them. We want to take this opportunity to thank the participants in this forum for their valuable input in informing the direction of Project Lambda to date and we look forward to continued collaboration with the community as we move into this next phase of the project. (It was nice to see so many of you at Devoxx this past week -- thanks for seeking me out and sharing your thoughts.) To multicore, and beyond! Brian Goetz Java Language Architect Oracle Corporation From brian.goetz at oracle.com Sat Nov 20 12:41:07 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 20 Nov 2010 15:41:07 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> Message-ID: <06C56FFC-A923-4D56-B999-1207F2E12169@oracle.com> (2) is an interesting idea and worth considering. On Nov 20, 2010, at 5:37 AM, Mikael Grev wrote: > Please don't make it possible to mutate the variable from within the closure. > > > However, I wouldn't mind if "effectively-final" was extended so that either: > > 1) The closure works on a copy, giving no restrictions on the initialization and reassignment of the captured variable > 2) Any variable is effectively-final as long as it isn't reassigned after the closure initialization. (reordering should be solvable) > > The reason for this is that the following isn't that uncommon and "i" needs to be reassigned as another variable to be effectively-final (ugly): > > int i = 0; > if (something) > i = xxx; > > capture of i > > Loosening the definition of effectively-final would effectively make the feature easier to use. > > Cheers, > Mikael > > > On Nov 20, 2010, at 10:29 AM, Brian Goetz wrote: > >>> Hi Brian, I came to you yesterday at devoxx to ask about the rationale >>> behind the decision to not capture mutable local variables. >> >> I admire your passion and your persistence :) >> >>> I understand that among several arguments, there is a strong one about >>> preventing people from doing unsafe things. you gave the analogy of >>> the kid, the fence and the pool. >> >> Safety mechanisms do not need to be perfect to be useful. Your arguments seem to center around "I can climb the fence, so there's no point in having it." This argument is, to be blunt, silly. The design principles of the Java language stress safety, even if there are cases where such safety guards can be "casted away" by a sufficiently motivated user. >> >> If you want to capture a not-effectively-final local variable in a closure, then one of the following two things has to happen: >> a) The lifetime of the variable must be extended to the lifetime of the closure >> b) The lifetime of the closure must be shortened to the lifetime of the variable. >> >> In both cases, we would want to ensure the variable is only accessed from the thread capturing the closure. In (a), we would be effectively creating a whole new class of variables (in addition to the seven (or eight, depending on how you count) already defined by the JLS); these are no longer local variables and should not look like them. In both (a) and (b), we would be creating a kind of restricted closure, whose execution is restricted in space (confined to a specific thread) and/or time (confined to the lifetime of the scope in which the variable is declared.) For concreteness let's call these confined variables and confined lambdas. >> >> It is possible to create mechanisms to reify and enforce these various types of confinement (we've discussed them extensively internally.) However, they add nontrivial complexity to the language. >> >> In order to justify the complexity that these new features would generate, there needs to be a compelling use case. When I explored this issue, I asked a number of people to write down a use case for this. Every one wrote some form of: >> >> int sum = 0; >> list.forEach( #{ x -> sum += x.foo() } >> >> In a parellel world, this idiom is irretrievably broken. (See Guy Steele's presentation "Organizing Functional Code for Parallel Execution, or, foldl considered slightly harmful.") People will do this, they will do this without thinking, and their code will be broken. It is very hard, even for experts, to get this right. While Java has plenty of other opportunities to create non-thread-safe code, I am not going to create an entirely new and nearly irresistible vector for doing so. Iteration and side-effects are how we've been trained to do things in Java, but we have to learn to do better. >> >> In sum, adding this feature that you (and others) want so badly seems to add up to: >> - Lots of new complexity (e.g., confined variables and lambdas) >> - Nearly irresistible new areas for making errors >> - All to prop up a broken programming model (iterations + side effects) >> >> To quote a past president: "Wouldn't be prudent." At this point in time, it feels the risk and complexity outweighs the benefit. I would rather put the effort into supporting map/reduce-y idioms in the libraries. The above block is much better as: >> >> list.reduce( #{ x, y => x+y } ) >> or >> list.reduce(Reducers.SUM) >> or >> list.sum() >> >>> this thought leads to another one. in your example you are assuming >>> that the foreach method will get into multithreaded stuff. >> >> Not quite: s/will/may/ >> >>> your foreach impl coud say: >>> >>> public void forEach(@Multithreaded Foreachable block) {...} >> >> We explored these issues in the JSR-166 expert group several years ago. The basic problem is that we don't have a way of reliably expressing "reference to thread-safe object" in the type system (either statically or dynamically), so these things revert to being documentation rather than type assertions. >> >>> for that matter, you should probably name your loop method forEachMT. >> >> Under consideration. Though my intuition is that (a) such a convention will be hard to stick to and (b) in five years it will probably look silly. > > From brian.goetz at oracle.com Sat Nov 20 12:44:09 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 20 Nov 2010 15:44:09 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> Message-ID: > I think I'm hearing that mutating a local variable from a closure is just a > bad idea in general right? For a closure that might conceivably be executed in another thread, this is correct. (Some would go farther and say such side-effects in general are bad.) > And the pattern of using a single element array > or an AtomicReference wrapper is dangerous and should be discouraged? Single-element array is disastrous since it seeks only to subvert the rules and doesn't provide needed synchronization. A (final) AtomicXxx might be OK, provided you used compareAndSet correctly for updates, since a final reference to a mutable thread-safe object is fair game (the thread-safe part is key.) The real offender is primitives. Adding up integers without even considering thread-safety is just so tempting.... From neal at gafter.com Sat Nov 20 13:08:53 2010 From: neal at gafter.com (Neal Gafter) Date: Sat, 20 Nov 2010 13:08:53 -0800 Subject: capturing (or not) mutable local variables In-Reply-To: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> Message-ID: On Sat, Nov 20, 2010 at 1:29 AM, Brian Goetz wrote: > If you want to capture a not-effectively-final local variable in a closure, > then one of the following two things has to happen: > a) The lifetime of the variable must be extended to the lifetime of the > closure > b) The lifetime of the closure must be shortened to the lifetime of the > variable. > The JLS doesn't talk about the lifetime of local variables because, like all variables and dynamically allocated objects, they simply cease to exist when they can no longer be referenced. There would be no need to change that. From brian.goetz at oracle.com Sat Nov 20 13:24:14 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 20 Nov 2010 16:24:14 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> Message-ID: <407E1F2F-84B2-4673-9ED6-C3B9F3D5B914@oracle.com> Surely you realize (a) would be a huge departure from the experience of typical java developers. Who do not read the jls. Sent from my iPhone On Nov 20, 2010, at 4:08 PM, Neal Gafter wrote: > On Sat, Nov 20, 2010 at 1:29 AM, Brian Goetz > wrote: > If you want to capture a not-effectively-final local variable in a > closure, then one of the following two things has to happen: > a) The lifetime of the variable must be extended to the lifetime of > the closure > b) The lifetime of the closure must be shortened to the lifetime of > the variable. > > The JLS doesn't talk about the lifetime of local variables because, > like all variables and dynamically allocated objects, they simply > cease to exist when they can no longer be referenced. There would > be no need to change that. > From jesse.sightler at gmail.com Sat Nov 20 17:28:00 2010 From: jesse.sightler at gmail.com (Jesse Sightler) Date: Sat, 20 Nov 2010 20:28:00 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: <407E1F2F-84B2-4673-9ED6-C3B9F3D5B914@oracle.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <407E1F2F-84B2-4673-9ED6-C3B9F3D5B914@oracle.com> Message-ID: As an average Java developer, I don't think so. It's just like anonymous inner classes, except with more easily permitted mutable state. The stuff about multithreaded accumulators is a major red herring IMO. On Nov 20, 2010 4:25 PM, "Brian Goetz" wrote: > Surely you realize (a) would be a huge departure from the experience > of typical java developers. Who do not read the jls. > > Sent from my iPhone > > On Nov 20, 2010, at 4:08 PM, Neal Gafter wrote: > >> On Sat, Nov 20, 2010 at 1:29 AM, Brian Goetz >> wrote: >> If you want to capture a not-effectively-final local variable in a >> closure, then one of the following two things has to happen: >> a) The lifetime of the variable must be extended to the lifetime of >> the closure >> b) The lifetime of the closure must be shortened to the lifetime of >> the variable. >> >> The JLS doesn't talk about the lifetime of local variables because, >> like all variables and dynamically allocated objects, they simply >> cease to exist when they can no longer be referenced. There would >> be no need to change that. >> > From neal at gafter.com Sat Nov 20 18:08:34 2010 From: neal at gafter.com (Neal Gafter) Date: Sat, 20 Nov 2010 18:08:34 -0800 Subject: capturing (or not) mutable local variables In-Reply-To: <407E1F2F-84B2-4673-9ED6-C3B9F3D5B914@oracle.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <407E1F2F-84B2-4673-9ED6-C3B9F3D5B914@oracle.com> Message-ID: Are you saying that if we restrict lambdas to only variables that don't change, then it is OK if the variable lifetime is not extended to the lifetime of the lambda? In that case, does the code blow up when you invoke a lambda that outlasts the block in which it was created? The point of my question is that mutable versus immutable has nothing to do with variable lifetime. Pretending they have anything to do with each other is just unnecessarily muddying the the discussion. And yes, I do realize that lambda expressions (and the resulting variable lifetime extension - no matter if the variables are final or not) are a departure from the experience of typical java developers. I also realize that programming with immutable data is a departure from the experience of typical java developers. On Sat, Nov 20, 2010 at 1:24 PM, Brian Goetz wrote: > Surely you realize (a) would be a huge departure from the experience of > typical java developers. Who do not read the jls. > > Sent from my iPhone > > On Nov 20, 2010, at 4:08 PM, Neal Gafter wrote: > > On Sat, Nov 20, 2010 at 1:29 AM, Brian Goetz < > brian.goetz at oracle.com> wrote: > >> If you want to capture a not-effectively-final local variable in a >> closure, then one of the following two things has to happen: >> a) The lifetime of the variable must be extended to the lifetime of the >> closure >> b) The lifetime of the closure must be shortened to the lifetime of the >> variable. >> > > The JLS doesn't talk about the lifetime of local variables because, like > all variables and dynamically allocated objects, they simply cease to exist > when they can no longer be referenced. There would be no need to change > that. > > From alex.blewitt at gmail.com Sun Nov 21 01:21:44 2010 From: alex.blewitt at gmail.com (Alex Blewitt) Date: Sun, 21 Nov 2010 09:21:44 +0000 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <407E1F2F-84B2-4673-9ED6-C3B9F3D5B914@oracle.com> Message-ID: On 21 Nov 2010, at 02:08, Neal Gafter wrote: > The point of my question is that mutable versus immutable has nothing to do > with variable lifetime. Pretending they have anything to do with each other > is just unnecessarily muddying the the discussion. Except that this assumes it is the same variable inside and outside the lambda. If it is semantically a different variable (with the lifetime of the closure) which has been assigned a copy of the value from the enclosing scope then mutability, or lack thereof, is key. > Alex From grev at miginfocom.com Sun Nov 21 03:39:03 2010 From: grev at miginfocom.com (Mikael Grev) Date: Sun, 21 Nov 2010 12:39:03 +0100 Subject: capturing (or not) mutable local variables In-Reply-To: <06C56FFC-A923-4D56-B999-1207F2E12169@oracle.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <06C56FFC-A923-4D56-B999-1207F2E12169@oracle.com> Message-ID: <86BC574E-205E-456D-89A3-2B94BDBFABEE@miginfocom.com> Excellent Brian. It would mean that a single compiler error would be shown when this is broken: - A variable cannot be changed after it has been used in a closure initialization. Easy, clear and user friendly. No need for funky hacks in the non-mutable case. This kind of capture will not be seen as limited as the current effectively-final definition, which only real merit is that it saves six keystrokes. And I think there's a big opportunity here as well. The spin doctor in me say that if it is carefully communicated by Oracle that 2) is a very conscious decision and that the way you should do capture of mutable variables is by using an AtomicXxx I think you can almost totally get away with it. Maybe even get out on top of other languages since this is very multi threading aware from the start. It is very important that all code examples for Lamdas have at least one example of a good use of AtomicXxx for mutable variables. Then this becomes the standard way of implementing mutability in parallel closures. There might even be room for some syntax sweetening of AtomicXxx usage in the future, which makes this even more compelling. Cheers, Mikael On Nov 20, 2010, at 21:41 PM, Brian Goetz wrote: > (2) is an interesting idea and worth considering. > > On Nov 20, 2010, at 5:37 AM, Mikael Grev wrote: > >> Please don't make it possible to mutate the variable from within the closure. >> >> >> However, I wouldn't mind if "effectively-final" was extended so that either: >> >> 1) The closure works on a copy, giving no restrictions on the initialization and reassignment of the captured variable >> 2) Any variable is effectively-final as long as it isn't reassigned after the closure initialization. (reordering should be solvable) >> >> The reason for this is that the following isn't that uncommon and "i" needs to be reassigned as another variable to be effectively-final (ugly): >> >> int i = 0; >> if (something) >> i = xxx; >> >> capture of i >> >> Loosening the definition of effectively-final would effectively make the feature easier to use. >> >> Cheers, >> Mikael >> >> >> On Nov 20, 2010, at 10:29 AM, Brian Goetz wrote: >> >>>> Hi Brian, I came to you yesterday at devoxx to ask about the rationale >>>> behind the decision to not capture mutable local variables. >>> >>> I admire your passion and your persistence :) >>> >>>> I understand that among several arguments, there is a strong one about >>>> preventing people from doing unsafe things. you gave the analogy of >>>> the kid, the fence and the pool. >>> >>> Safety mechanisms do not need to be perfect to be useful. Your arguments seem to center around "I can climb the fence, so there's no point in having it." This argument is, to be blunt, silly. The design principles of the Java language stress safety, even if there are cases where such safety guards can be "casted away" by a sufficiently motivated user. >>> >>> If you want to capture a not-effectively-final local variable in a closure, then one of the following two things has to happen: >>> a) The lifetime of the variable must be extended to the lifetime of the closure >>> b) The lifetime of the closure must be shortened to the lifetime of the variable. >>> >>> In both cases, we would want to ensure the variable is only accessed from the thread capturing the closure. In (a), we would be effectively creating a whole new class of variables (in addition to the seven (or eight, depending on how you count) already defined by the JLS); these are no longer local variables and should not look like them. In both (a) and (b), we would be creating a kind of restricted closure, whose execution is restricted in space (confined to a specific thread) and/or time (confined to the lifetime of the scope in which the variable is declared.) For concreteness let's call these confined variables and confined lambdas. >>> >>> It is possible to create mechanisms to reify and enforce these various types of confinement (we've discussed them extensively internally.) However, they add nontrivial complexity to the language. >>> >>> In order to justify the complexity that these new features would generate, there needs to be a compelling use case. When I explored this issue, I asked a number of people to write down a use case for this. Every one wrote some form of: >>> >>> int sum = 0; >>> list.forEach( #{ x -> sum += x.foo() } >>> >>> In a parellel world, this idiom is irretrievably broken. (See Guy Steele's presentation "Organizing Functional Code for Parallel Execution, or, foldl considered slightly harmful.") People will do this, they will do this without thinking, and their code will be broken. It is very hard, even for experts, to get this right. While Java has plenty of other opportunities to create non-thread-safe code, I am not going to create an entirely new and nearly irresistible vector for doing so. Iteration and side-effects are how we've been trained to do things in Java, but we have to learn to do better. >>> >>> In sum, adding this feature that you (and others) want so badly seems to add up to: >>> - Lots of new complexity (e.g., confined variables and lambdas) >>> - Nearly irresistible new areas for making errors >>> - All to prop up a broken programming model (iterations + side effects) >>> >>> To quote a past president: "Wouldn't be prudent." At this point in time, it feels the risk and complexity outweighs the benefit. I would rather put the effort into supporting map/reduce-y idioms in the libraries. The above block is much better as: >>> >>> list.reduce( #{ x, y => x+y } ) >>> or >>> list.reduce(Reducers.SUM) >>> or >>> list.sum() >>> >>>> this thought leads to another one. in your example you are assuming >>>> that the foreach method will get into multithreaded stuff. >>> >>> Not quite: s/will/may/ >>> >>>> your foreach impl coud say: >>>> >>>> public void forEach(@Multithreaded Foreachable block) {...} >>> >>> We explored these issues in the JSR-166 expert group several years ago. The basic problem is that we don't have a way of reliably expressing "reference to thread-safe object" in the type system (either statically or dynamically), so these things revert to being documentation rather than type assertions. >>> >>>> for that matter, you should probably name your loop method forEachMT. >>> >>> Under consideration. Though my intuition is that (a) such a convention will be hard to stick to and (b) in five years it will probably look silly. >> >> From neal at gafter.com Sun Nov 21 07:06:16 2010 From: neal at gafter.com (Neal Gafter) Date: Sun, 21 Nov 2010 07:06:16 -0800 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <407E1F2F-84B2-4673-9ED6-C3B9F3D5B914@oracle.com> Message-ID: On Sun, Nov 21, 2010 at 1:21 AM, Alex Blewitt wrote: > On 21 Nov 2010, at 02:08, Neal Gafter wrote: > > > The point of my question is that mutable versus immutable has nothing to > do > > with variable lifetime. Pretending they have anything to do with each > other > > is just unnecessarily muddying the the discussion. > > Except that this assumes it is the same variable inside and outside the > lambda. If it is semantically a different variable (with the lifetime of the > closure) which has been assigned a copy of the value from the enclosing > scope then mutability, or lack thereof, is key. > Is it semantically different? It appears in all ways to be a single variable throughout its scope. The "lifetime of the variable" isn't part of the semantics today, and if you distinguish one part of the variable's scope from another by introducing this concept, you haven't added anything useful to the description. Do you mean to intend to allow the lambda to change the value of its copy? If so, your description would have some meaning. If not, you're suggesting a difference without a distinctions. From brian.goetz at oracle.com Sun Nov 21 07:50:50 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 21 Nov 2010 10:50:50 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: <86BC574E-205E-456D-89A3-2B94BDBFABEE@miginfocom.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <06C56FFC-A923-4D56-B999-1207F2E12169@oracle.com> <86BC574E-205E-456D-89A3-2B94BDBFABEE@miginfocom.com> Message-ID: <4CE93FDA.3050907@oracle.com> You can think of effectively final as a form of type inference. There are lots of aspects of type inference coming in Java (diamond from Coin, target typing for lambda formals) and inferring finality is yet another. On 11/21/2010 6:39 AM, Mikael Grev wrote: > Excellent Brian. > > It would mean that a single compiler error would be shown when this is broken: > > - A variable cannot be changed after it has been used in a closure initialization. > > Easy, clear and user friendly. No need for funky hacks in the non-mutable case. > > This kind of capture will not be seen as limited as the current effectively-final definition, which only real merit is that it saves six keystrokes. > > > And I think there's a big opportunity here as well. The spin doctor in me say that if it is carefully communicated by Oracle that 2) is a very conscious decision and that the way you should do capture of mutable variables is by using an AtomicXxx I think you can almost totally get away with it. Maybe even get out on top of other languages since this is very multi threading aware from the start. > It is very important that all code examples for Lamdas have at least one example of a good use of AtomicXxx for mutable variables. Then this becomes the standard way of implementing mutability in parallel closures. There might even be room for some syntax sweetening of AtomicXxx usage in the future, which makes this even more compelling. > > Cheers, > Mikael > > > On Nov 20, 2010, at 21:41 PM, Brian Goetz wrote: > >> (2) is an interesting idea and worth considering. >> >> On Nov 20, 2010, at 5:37 AM, Mikael Grev wrote: >> >>> Please don't make it possible to mutate the variable from within the closure. >>> >>> >>> However, I wouldn't mind if "effectively-final" was extended so that either: >>> >>> 1) The closure works on a copy, giving no restrictions on the initialization and reassignment of the captured variable >>> 2) Any variable is effectively-final as long as it isn't reassigned after the closure initialization. (reordering should be solvable) >>> >>> The reason for this is that the following isn't that uncommon and "i" needs to be reassigned as another variable to be effectively-final (ugly): >>> >>> int i = 0; >>> if (something) >>> i = xxx; >>> >>> capture of i >>> >>> Loosening the definition of effectively-final would effectively make the feature easier to use. >>> >>> Cheers, >>> Mikael >>> >>> >>> On Nov 20, 2010, at 10:29 AM, Brian Goetz wrote: >>> >>>>> Hi Brian, I came to you yesterday at devoxx to ask about the rationale >>>>> behind the decision to not capture mutable local variables. >>>> >>>> I admire your passion and your persistence :) >>>> >>>>> I understand that among several arguments, there is a strong one about >>>>> preventing people from doing unsafe things. you gave the analogy of >>>>> the kid, the fence and the pool. >>>> >>>> Safety mechanisms do not need to be perfect to be useful. Your arguments seem to center around "I can climb the fence, so there's no point in having it." This argument is, to be blunt, silly. The design principles of the Java language stress safety, even if there are cases where such safety guards can be "casted away" by a sufficiently motivated user. >>>> >>>> If you want to capture a not-effectively-final local variable in a closure, then one of the following two things has to happen: >>>> a) The lifetime of the variable must be extended to the lifetime of the closure >>>> b) The lifetime of the closure must be shortened to the lifetime of the variable. >>>> >>>> In both cases, we would want to ensure the variable is only accessed from the thread capturing the closure. In (a), we would be effectively creating a whole new class of variables (in addition to the seven (or eight, depending on how you count) already defined by the JLS); these are no longer local variables and should not look like them. In both (a) and (b), we would be creating a kind of restricted closure, whose execution is restricted in space (confined to a specific thread) and/or time (confined to the lifetime of the scope in which the variable is declared.) For concreteness let's call these confined variables and confined lambdas. >>>> >>>> It is possible to create mechanisms to reify and enforce these various types of confinement (we've discussed them extensively internally.) However, they add nontrivial complexity to the language. >>>> >>>> In order to justify the complexity that these new features would generate, there needs to be a compelling use case. When I explored this issue, I asked a number of people to write down a use case for this. Every one wrote some form of: >>>> >>>> int sum = 0; >>>> list.forEach( #{ x -> sum += x.foo() } >>>> >>>> In a parellel world, this idiom is irretrievably broken. (See Guy Steele's presentation "Organizing Functional Code for Parallel Execution, or, foldl considered slightly harmful.") People will do this, they will do this without thinking, and their code will be broken. It is very hard, even for experts, to get this right. While Java has plenty of other opportunities to create non-thread-safe code, I am not going to create an entirely new and nearly irresistible vector for doing so. Iteration and side-effects are how we've been trained to do things in Java, but we have to learn to do better. >>>> >>>> In sum, adding this feature that you (and others) want so badly seems to add up to: >>>> - Lots of new complexity (e.g., confined variables and lambdas) >>>> - Nearly irresistible new areas for making errors >>>> - All to prop up a broken programming model (iterations + side effects) >>>> >>>> To quote a past president: "Wouldn't be prudent." At this point in time, it feels the risk and complexity outweighs the benefit. I would rather put the effort into supporting map/reduce-y idioms in the libraries. The above block is much better as: >>>> >>>> list.reduce( #{ x, y => x+y } ) >>>> or >>>> list.reduce(Reducers.SUM) >>>> or >>>> list.sum() >>>> >>>>> this thought leads to another one. in your example you are assuming >>>>> that the foreach method will get into multithreaded stuff. >>>> >>>> Not quite: s/will/may/ >>>> >>>>> your foreach impl coud say: >>>>> >>>>> public void forEach(@Multithreaded Foreachable block) {...} >>>> >>>> We explored these issues in the JSR-166 expert group several years ago. The basic problem is that we don't have a way of reliably expressing "reference to thread-safe object" in the type system (either statically or dynamically), so these things revert to being documentation rather than type assertions. >>>> >>>>> for that matter, you should probably name your loop method forEachMT. >>>> >>>> Under consideration. Though my intuition is that (a) such a convention will be hard to stick to and (b) in five years it will probably look silly. >>> >>> > > From per at bothner.com Sun Nov 21 12:18:16 2010 From: per at bothner.com (Per Bothner) Date: Sun, 21 Nov 2010 12:18:16 -0800 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> Message-ID: <4CE97E88.5010803@bothner.com> On 11/20/2010 12:44 PM, Brian Goetz wrote: >> I think I'm hearing that mutating a local variable from a closure is just a >> bad idea in general right? > > For a closure that might conceivably be executed in another thread, this is correct. Agreed - but how is that different from mutating a field or array element from a closure that might conceivably be executed in another thread? You're just forcing the user to do by hand to work around a restriction. Note this is not a restriction in any other language I know that has both closures and mutable local variables. The traditional concept of lexical scoping (as in Lisp and Scheme) is that closures capture local bindings. If you disallow closing over mutable locals, then you only provide a restricted subset of closures. Of course having multiple threads invoking a closure that can mutate a variable is Bad, but what I'm not getting how this is different from multiple threads invoking a method that mutate a field. Yes, it might be nice to have a language that protects against the latter - but that is different language than Java. > Single-element array is disastrous since it seeks only to subvert the rules and doesn't provide needed synchronization. Which leads to the question: Since the rules as so trivially subvertable, why have the rules, since that they complicate real use-cases? Java really isn't a language for true multi-threaded programming - it's a language where multiple threads can co-ordinate with each other (when using appropriate care). So making "multiple threads" the justification for this restriction doesn't really make multi-threaded programming easier or safer. -- --Per Bothner per at bothner.com http://per.bothner.com/ From peter.levart at gmail.com Sun Nov 21 12:46:29 2010 From: peter.levart at gmail.com (Peter Levart) Date: Sun, 21 Nov 2010 21:46:29 +0100 Subject: capturing (or not) mutable local variables In-Reply-To: References: <407E1F2F-84B2-4673-9ED6-C3B9F3D5B914@oracle.com> Message-ID: <201011212146.30228.peter.levart@marand.si> On Sunday, November 21, 2010 03:08:34 am Neal Gafter wrote: > Are you saying that if we restrict lambdas to only variables that don't > change, then it is OK if the variable lifetime is not extended to the > lifetime of the lambda? In that case, does the code blow up when you > invoke a lambda that outlasts the block in which it was created? > > The point of my question is that mutable versus immutable has nothing to do > with variable lifetime. Pretending they have anything to do with each > other is just unnecessarily muddying the the discussion. That's true. But not entirely. Currently any mutable variable that can be accessed from multiple threads (instance or static) can be marked as volatile. Local variables can't be marked as volatile. That "restriction" would have to be lifted. Regards, Peter > > And yes, I do realize that lambda expressions (and the resulting variable > lifetime extension - no matter if the variables are final or not) are a > departure from the experience of typical java developers. I also realize > that programming with immutable data is a departure from the experience of > typical java developers. > > On Sat, Nov 20, 2010 at 1:24 PM, Brian Goetz wrote: > > Surely you realize (a) would be a huge departure from the experience of > > typical java developers. Who do not read the jls. > > > > Sent from my iPhone > > > > On Nov 20, 2010, at 4:08 PM, Neal Gafter wrote: > > > > On Sat, Nov 20, 2010 at 1:29 AM, Brian Goetz < > > > > brian.goetz at oracle.com> wrote: > >> If you want to capture a not-effectively-final local variable in a > >> closure, then one of the following two things has to happen: > >> a) The lifetime of the variable must be extended to the lifetime of the > >> closure > >> b) The lifetime of the closure must be shortened to the lifetime of the > >> variable. > > > > The JLS doesn't talk about the lifetime of local variables because, like > > all variables and dynamically allocated objects, they simply cease to > > exist when they can no longer be referenced. There would be no need to > > change that. From jim at pentastich.org Sun Nov 21 14:41:09 2010 From: jim at pentastich.org (Jim Mayer) Date: Sun, 21 Nov 2010 17:41:09 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: <4CE97E88.5010803@bothner.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> <4CE97E88.5010803@bothner.com> Message-ID: I completely agree with Mr. Bothner's analysis. If I understand correctly, the motivation for disallowing access to mutable local variables is to make libraries that transparently use multiple threads (e.g., parallel array operations) safer. I think the restriction unnecessarily limits the functionality of closures in the (far more common today) single-threaded cases and is insufficient to make the multi-threaded case actually safe. I like the idea of an annotation on methods (@Parallel?) that could be used by the compiler on a "best effort" basis to warn of common unsafe practices (e.g., any unsynchronized access to mutable state) would help. I assume that's been proposed and debated many times already, though :-) --- Jim On Sun, Nov 21, 2010 at 3:18 PM, Per Bothner wrote: > On 11/20/2010 12:44 PM, Brian Goetz wrote: > >> I think I'm hearing that mutating a local variable from a closure is > just a > >> bad idea in general right? > > > > For a closure that might conceivably be executed in another thread, this > is correct. > > Agreed - but how is that different from mutating a field or array element > from a closure that might conceivably be executed in another thread? > You're just forcing the user to do by hand to work around a restriction. > > Note this is not a restriction in any other language I know that has > both closures and mutable local variables. The traditional concept of > lexical scoping (as in Lisp and Scheme) is that closures capture > local bindings. If you disallow closing over mutable locals, > then you only provide a restricted subset of closures. > > Of course having multiple threads invoking a closure that can > mutate a variable is Bad, but what I'm not getting how this is > different from multiple threads invoking a method that mutate > a field. Yes, it might be nice to have a language that protects > against the latter - but that is different language than Java. > > > Single-element array is disastrous since it seeks only to subvert the > rules and doesn't provide needed synchronization. > > Which leads to the question: Since the rules as so trivially > subvertable, why have the > rules, since that they complicate real use-cases? Java really isn't a > language > for true multi-threaded programming - it's a language where multiple > threads > can co-ordinate with each other (when using appropriate care). So making > "multiple threads" the justification for this restriction doesn't really > make multi-threaded programming easier or safer. > -- > --Per Bothner > per at bothner.com http://per.bothner.com/ > > From forax at univ-mlv.fr Sun Nov 21 16:32:29 2010 From: forax at univ-mlv.fr (=?ISO-8859-1?Q?R=E9mi_Forax?=) Date: Mon, 22 Nov 2010 01:32:29 +0100 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> <4CE97E88.5010803@bothner.com> Message-ID: <4CE9BA1D.6010307@univ-mlv.fr> Le 21/11/2010 23:41, Jim Mayer a ?crit : > I completely agree with Mr. Bothner's analysis. > > If I understand correctly, the motivation for disallowing access to mutable > local variables is to make libraries that transparently use multiple threads > (e.g., parallel array operations) safer. I think the restriction > unnecessarily limits the functionality of closures in the (far more common > today) single-threaded cases and is insufficient to make the multi-threaded > case actually safe. > I disagree. Let says that we don't want lambda for today but for tomorrow. Tomorrow all computers will have a lot of cores (I receive a new laptop last Friday and it has 8 cores) so the JDK needs parallel collection APIs. I think it's stupid to say that lambdas will capture variables and not values and in the same time introduces a parallel API in the JDK. About a Collections.foreach, from my point of view, it should not be included in the JDK. We should use map/reduce instead and the APIs will do all the parallel magic for us. I understand that this is a shift in the way of we currently think Java programs. But I don't think we have a choice, at least until Intel decides to replace all the cores of my laptop with one big core. > I like the idea of an annotation on methods (@Parallel?) that could be used > by the compiler on a > "best effort" basis to warn of common unsafe practices (e.g., any > unsynchronized access to mutable state) would help. I assume that's been > proposed and debated many times already, though :-) > > --- Jim > R?mi > > On Sun, Nov 21, 2010 at 3:18 PM, Per Bothner wrote: > > >> On 11/20/2010 12:44 PM, Brian Goetz wrote: >> >>>> I think I'm hearing that mutating a local variable from a closure is >>>> >> just a >> >>>> bad idea in general right? >>>> >>> For a closure that might conceivably be executed in another thread, this >>> >> is correct. >> >> Agreed - but how is that different from mutating a field or array element >> from a closure that might conceivably be executed in another thread? >> You're just forcing the user to do by hand to work around a restriction. >> >> Note this is not a restriction in any other language I know that has >> both closures and mutable local variables. The traditional concept of >> lexical scoping (as in Lisp and Scheme) is that closures capture >> local bindings. If you disallow closing over mutable locals, >> then you only provide a restricted subset of closures. >> >> Of course having multiple threads invoking a closure that can >> mutate a variable is Bad, but what I'm not getting how this is >> different from multiple threads invoking a method that mutate >> a field. Yes, it might be nice to have a language that protects >> against the latter - but that is different language than Java. >> >> >>> Single-element array is disastrous since it seeks only to subvert the >>> >> rules and doesn't provide needed synchronization. >> >> Which leads to the question: Since the rules as so trivially >> subvertable, why have the >> rules, since that they complicate real use-cases? Java really isn't a >> language >> for true multi-threaded programming - it's a language where multiple >> threads >> can co-ordinate with each other (when using appropriate care). So making >> "multiple threads" the justification for this restriction doesn't really >> make multi-threaded programming easier or safer. >> -- >> --Per Bothner >> per at bothner.com http://per.bothner.com/ >> >> >> > From howard.lovatt at gmail.com Sun Nov 21 17:45:27 2010 From: howard.lovatt at gmail.com (Howard Lovatt) Date: Mon, 22 Nov 2010 12:45:27 +1100 Subject: capturing (or not) mutable local variables Message-ID: I think Brian and Co. have got this one right; the future is multicore and parallel processing, no point introducing something that will be an ever increasing pain point. We should be looking to our parallel future, not our serial past. I should state my bias, in that I like a functional approach and I am much more influenced by the likes of Haskell than Lisp. I also find the argument that you can't have closures without writing to variables strange since languages like Haskell are normally treated as having closures but don't have write access. Also in Church's Lambda calculus there was no write at all! > ---------- Forwarded message ---------- > From:?Mikael Grev > To:?lambda-dev at openjdk.java.net > Date:?Sun, 21 Nov 2010 12:39:03 +0100 > Subject:?Re: capturing (or not) mutable local variables > Excellent Brian. > > It would mean that a single compiler error would be shown when this is broken: > > ?- A variable cannot be changed after it has been used in a closure initialization. > > Easy, clear and user friendly. No need for funky hacks in the non-mutable case. > > This kind of capture will not be seen as limited as the current effectively-final definition, which only real merit is that it saves six keystrokes. > > > And I think there's a big opportunity here as well. The spin doctor in me say that if it is carefully communicated by Oracle that 2) is a very conscious decision and that the way you should do capture of mutable variables is by using an AtomicXxx I think you can almost totally get away with it. Maybe even get out on top of other languages since this is very multi threading aware from the start. > It is very important that all code examples for Lamdas have at least one example of a good use of AtomicXxx for mutable variables. Then this becomes the standard way of implementing mutability in parallel closures. There might even be room for some syntax sweetening of AtomicXxx usage in the future, which makes this even more compelling. > > Cheers, > Mikael > > > On Nov 20, 2010, at 21:41 PM, Brian Goetz wrote: > >> (2) is an interesting idea and worth considering. >> >> On Nov 20, 2010, at 5:37 AM, Mikael Grev wrote: >> >>> Please don't make it possible to mutate the variable from within the closure. >>> >>> >>> However, I wouldn't mind if "effectively-final" was extended so that either: >>> >>> 1) The closure works on a copy, giving no restrictions on the initialization and reassignment of the captured variable >>> 2) Any variable is effectively-final as long as it isn't reassigned after the closure initialization. (reordering should be solvable) >>> >>> The reason for this is that the following isn't that uncommon and "i" needs to be reassigned as another variable to be effectively-final (ugly): >>> >>> int i = 0; >>> if (something) >>> ? ? ?i = xxx; >>> >>> capture of i >>> >>> Loosening the definition of effectively-final would effectively make the feature easier to use. >>> >>> Cheers, >>> Mikael >>> >>> >>> On Nov 20, 2010, at 10:29 AM, Brian Goetz wrote: >>> >>>>> Hi Brian, I came to you yesterday at devoxx to ask about the rationale >>>>> behind the decision to not capture mutable local variables. >>>> >>>> I admire your passion and your persistence :) >>>> >>>>> I understand that among several arguments, there is a strong one about >>>>> preventing people from doing unsafe things. you gave the analogy of >>>>> the kid, the fence and the pool. >>>> >>>> Safety mechanisms do not need to be perfect to be useful. ?Your arguments seem to center around "I can climb the fence, so there's no point in having it." ?This argument is, to be blunt, silly. ?The design principles of the Java language stress safety, even if there are cases where such safety guards can be "casted away" by a sufficiently motivated user. >>>> >>>> If you want to capture a not-effectively-final local variable in a closure, then one of the following two things has to happen: >>>> a) The lifetime of the variable must be extended to the lifetime of the closure >>>> b) The lifetime of the closure must be shortened to the lifetime of the variable. >>>> >>>> In both cases, we would want to ensure the variable is only accessed from the thread capturing the closure. ?In (a), we would be effectively creating a whole new class of variables (in addition to the seven (or eight, depending on how you count) already defined by the JLS); these are no longer local variables and should not look like them. ? In both (a) and (b), we would be creating a kind of restricted closure, whose execution is restricted in space (confined to a specific thread) and/or time (confined to the lifetime of the scope in which the variable is declared.) ?For concreteness let's call these confined variables and confined lambdas. >>>> >>>> It is possible to create mechanisms to reify and enforce these various types of confinement (we've discussed them extensively internally.) ?However, they add nontrivial complexity to the language. >>>> >>>> In order to justify the complexity that these new features would generate, there needs to be a compelling use case. ?When I explored this issue, I asked a number of people to write down a use case for this. ?Every one wrote some form of: >>>> >>>> int sum = 0; >>>> list.forEach( #{ x -> sum += x.foo() } >>>> >>>> In a parellel world, this idiom is irretrievably broken. ?(See Guy Steele's presentation "Organizing Functional Code for Parallel Execution, or, foldl considered slightly harmful.") ?People will do this, they will do this without thinking, and their code will be broken. ?It is very hard, even for experts, to get this right. ?While Java has plenty of other opportunities to create non-thread-safe code, I am not going to create an entirely new and nearly irresistible vector for doing so. ?Iteration and side-effects are how we've been trained to do things in Java, but we have to learn to do better. >>>> >>>> In sum, adding this feature that you (and others) want so badly seems to add up to: >>>> - Lots of new complexity (e.g., confined variables and lambdas) >>>> - Nearly irresistible new areas for making errors >>>> - All to prop up a broken programming model (iterations + side effects) >>>> >>>> To quote a past president: "Wouldn't be prudent." ?At this point in time, it feels the risk and complexity outweighs the benefit. ?I would rather put the effort into supporting map/reduce-y idioms in the libraries. ?The above block is much better as: >>>> >>>> list.reduce( #{ x, y => x+y } ) >>>> or >>>> list.reduce(Reducers.SUM) >>>> or >>>> list.sum() >>>> >>>>> this thought leads to another one. in your example you are assuming >>>>> that the foreach method will get into multithreaded stuff. >>>> >>>> Not quite: s/will/may/ >>>> >>>>> your foreach impl coud say: >>>>> >>>>> public void forEach(@Multithreaded Foreachable block) {...} >>>> >>>> We explored these issues in the JSR-166 expert group several years ago. ?The basic problem is that we don't have a way of reliably expressing "reference to thread-safe object" in the type system (either statically or dynamically), so these things revert to being documentation rather than type assertions. >>>> >>>>> for that matter, you should probably name your loop method forEachMT. >>>> >>>> Under consideration. ?Though my intuition is that (a) such a convention will be hard to stick to and (b) in five years it will probably look silly. >>> >>> > > > > > ---------- Forwarded message ---------- > From:?Neal Gafter > To:?Alex Blewitt > Date:?Sun, 21 Nov 2010 07:06:16 -0800 > Subject:?Re: capturing (or not) mutable local variables > On Sun, Nov 21, 2010 at 1:21 AM, Alex Blewitt wrote: > >> On 21 Nov 2010, at 02:08, Neal Gafter wrote: >> >> > The point of my question is that mutable versus immutable has nothing to >> do >> > with variable lifetime. ?Pretending they have anything to do with each >> other >> > is just unnecessarily muddying the the discussion. >> >> Except that this assumes it is the same variable inside and outside the >> lambda. If it is semantically a different variable (with the lifetime of the >> closure) which has been assigned a copy of the value from the enclosing >> scope then mutability, or lack thereof, is key. >> > > Is it semantically different? ?It appears in all ways to be a single > variable throughout its scope. ?The "lifetime of the variable" isn't part of > the semantics today, and if you distinguish one part of the variable's scope > from another by introducing this concept, you haven't added anything useful > to the description. > > Do you mean to intend to allow the lambda to change the value of its copy? > If so, your description would have some meaning. ?If not, you're suggesting > a difference without a distinctions. > > > > ---------- Forwarded message ---------- > From:?Brian Goetz > To:?Mikael Grev > Date:?Sun, 21 Nov 2010 10:50:50 -0500 > Subject:?Re: capturing (or not) mutable local variables > You can think of effectively final as a form of type inference. ?There are lots of aspects of type inference coming in Java (diamond from Coin, target typing for lambda formals) and inferring finality is yet another. > > On 11/21/2010 6:39 AM, Mikael Grev wrote: >> >> Excellent Brian. >> >> It would mean that a single compiler error would be shown when this is broken: >> >> ? - A variable cannot be changed after it has been used in a closure initialization. >> >> Easy, clear and user friendly. No need for funky hacks in the non-mutable case. >> >> This kind of capture will not be seen as limited as the current effectively-final definition, which only real merit is that it saves six keystrokes. >> >> >> And I think there's a big opportunity here as well. The spin doctor in me say that if it is carefully communicated by Oracle that 2) is a very conscious decision and that the way you should do capture of mutable variables is by using an AtomicXxx I think you can almost totally get away with it. Maybe even get out on top of other languages since this is very multi threading aware from the start. >> It is very important that all code examples for Lamdas have at least one example of a good use of AtomicXxx for mutable variables. Then this becomes the standard way of implementing mutability in parallel closures. There might even be room for some syntax sweetening of AtomicXxx usage in the future, which makes this even more compelling. >> >> Cheers, >> Mikael >> >> >> On Nov 20, 2010, at 21:41 PM, Brian Goetz wrote: >> >>> (2) is an interesting idea and worth considering. >>> >>> On Nov 20, 2010, at 5:37 AM, Mikael Grev wrote: >>> >>>> Please don't make it possible to mutate the variable from within the closure. >>>> >>>> >>>> However, I wouldn't mind if "effectively-final" was extended so that either: >>>> >>>> 1) The closure works on a copy, giving no restrictions on the initialization and reassignment of the captured variable >>>> 2) Any variable is effectively-final as long as it isn't reassigned after the closure initialization. (reordering should be solvable) >>>> >>>> The reason for this is that the following isn't that uncommon and "i" needs to be reassigned as another variable to be effectively-final (ugly): >>>> >>>> int i = 0; >>>> if (something) >>>> ? ? ? ?i = xxx; >>>> >>>> capture of i >>>> >>>> Loosening the definition of effectively-final would effectively make the feature easier to use. >>>> >>>> Cheers, >>>> Mikael >>>> >>>> >>>> On Nov 20, 2010, at 10:29 AM, Brian Goetz wrote: >>>> >>>>>> Hi Brian, I came to you yesterday at devoxx to ask about the rationale >>>>>> behind the decision to not capture mutable local variables. >>>>> >>>>> I admire your passion and your persistence :) >>>>> >>>>>> I understand that among several arguments, there is a strong one about >>>>>> preventing people from doing unsafe things. you gave the analogy of >>>>>> the kid, the fence and the pool. >>>>> >>>>> Safety mechanisms do not need to be perfect to be useful. ?Your arguments seem to center around "I can climb the fence, so there's no point in having it." ?This argument is, to be blunt, silly. ?The design principles of the Java language stress safety, even if there are cases where such safety guards can be "casted away" by a sufficiently motivated user. >>>>> >>>>> If you want to capture a not-effectively-final local variable in a closure, then one of the following two things has to happen: >>>>> a) The lifetime of the variable must be extended to the lifetime of the closure >>>>> b) The lifetime of the closure must be shortened to the lifetime of the variable. >>>>> >>>>> In both cases, we would want to ensure the variable is only accessed from the thread capturing the closure. ?In (a), we would be effectively creating a whole new class of variables (in addition to the seven (or eight, depending on how you count) already defined by the JLS); these are no longer local variables and should not look like them. ? In both (a) and (b), we would be creating a kind of restricted closure, whose execution is restricted in space (confined to a specific thread) and/or time (confined to the lifetime of the scope in which the variable is declared.) ?For concreteness let's call these confined variables and confined lambdas. >>>>> >>>>> It is possible to create mechanisms to reify and enforce these various types of confinement (we've discussed them extensively internally.) ?However, they add nontrivial complexity to the language. >>>>> >>>>> In order to justify the complexity that these new features would generate, there needs to be a compelling use case. ?When I explored this issue, I asked a number of people to write down a use case for this. ?Every one wrote some form of: >>>>> >>>>> int sum = 0; >>>>> list.forEach( #{ x -> ?sum += x.foo() } >>>>> >>>>> In a parellel world, this idiom is irretrievably broken. ?(See Guy Steele's presentation "Organizing Functional Code for Parallel Execution, or, foldl considered slightly harmful.") ?People will do this, they will do this without thinking, and their code will be broken. ?It is very hard, even for experts, to get this right. ?While Java has plenty of other opportunities to create non-thread-safe code, I am not going to create an entirely new and nearly irresistible vector for doing so. ?Iteration and side-effects are how we've been trained to do things in Java, but we have to learn to do better. >>>>> >>>>> In sum, adding this feature that you (and others) want so badly seems to add up to: >>>>> - Lots of new complexity (e.g., confined variables and lambdas) >>>>> - Nearly irresistible new areas for making errors >>>>> - All to prop up a broken programming model (iterations + side effects) >>>>> >>>>> To quote a past president: "Wouldn't be prudent." ?At this point in time, it feels the risk and complexity outweighs the benefit. ?I would rather put the effort into supporting map/reduce-y idioms in the libraries. ?The above block is much better as: >>>>> >>>>> list.reduce( #{ x, y => ?x+y } ) >>>>> or >>>>> list.reduce(Reducers.SUM) >>>>> or >>>>> list.sum() >>>>> >>>>>> this thought leads to another one. in your example you are assuming >>>>>> that the foreach method will get into multithreaded stuff. >>>>> >>>>> Not quite: s/will/may/ >>>>> >>>>>> your foreach impl coud say: >>>>>> >>>>>> public void forEach(@Multithreaded Foreachable block) {...} >>>>> >>>>> We explored these issues in the JSR-166 expert group several years ago. ?The basic problem is that we don't have a way of reliably expressing "reference to thread-safe object" in the type system (either statically or dynamically), so these things revert to being documentation rather than type assertions. >>>>> >>>>>> for that matter, you should probably name your loop method forEachMT. >>>>> >>>>> Under consideration. ?Though my intuition is that (a) such a convention will be hard to stick to and (b) in five years it will probably look silly. >>>> >>>> >> >> > > > -- ? -- Howard. From int19h at gmail.com Sun Nov 21 19:10:12 2010 From: int19h at gmail.com (Pavel Minaev) Date: Sun, 21 Nov 2010 19:10:12 -0800 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: On Sun, Nov 21, 2010 at 5:45 PM, Howard Lovatt wrote: > I think Brian and Co. have got this one right; the future is multicore > and parallel processing, no point introducing something that will be > an ever increasing pain point. We should be looking to our parallel > future, not our serial past. It just seems strange to do so in one particular - and relatively small - corner of a language that otherwise heavily promoted imperative approach and mutable state. Looking out to parallel future requires languages designed from ground up to conform to that model. Java is not such a language. It cannot be reasonably be made into such while remaining the same language in anything but name. As others have noted already, it is especially strange to restrict one particular use case (mutable locals), but leave others (mutable fields, including indirectly via getters/setters) - which are just as likely to occur - be. If anything, this may provoke the user of the language into false sense of security early on, "if it compiles then I'm not doing anything wrong" - and then trip him over when it comes to just slightly more complex code. Overall, it really seems that the very broad feature such as closures is being deliberately narrowed down to some very specific use cases to justify the constraints. I find the claim that closures will mainly be used in multithreaded context rather dubious - of all other mainstream languages which have closures that I know of, the majority of uses are in single-threaded context. If we look at C#, as the language that is closest to Java both semantically and in terms of intended use scenarios, again most uses of lambdas there are for more concise and clear code, and not for parallelization. Future may change that, but I think that for a mature language such as Java, designing language features with a crystal ball in hand is ill-advised. Far better to note how the same feature is designed and used elsewhere _today_, and build on that. > I should state my bias, in that I like a functional approach and I am > much more influenced by the likes of Haskell than Lisp. I also find > the argument that you can't have closures without writing to variables > strange since languages like Haskell are normally treated as having > closures but don't have write access. Also in Church's Lambda calculus > there was no write at all! The difference is that Haskell (and other similar cases) does not have mutable variables in general, so of course it wouldn't have that in closures, either. In that respect, it is fully consistent. However, all languages - including those with traditional functional background, such as Scheme or ML - which have mutable locals, also capture that mutability in closures. This is simply by definition of closure, which is a capture of the environment - if said environment is mutable, then that aspect of it should also be closed over. From jim at pentastich.org Sun Nov 21 20:25:06 2010 From: jim at pentastich.org (Jim Mayer) Date: Sun, 21 Nov 2010 23:25:06 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: Actually, I am extremely fond of functional programming and lazy evaluation. It's one of my standard "tricks" and I use it a lot in my Java code. Haskell (which I've read about but haven't used) gracefully incorporates the need for interacting with a mutable world by encapsulating "impure" computations. I think it would be interesting to embed a functional language within Java (like a monad in Haskell turned on its head :-)). The problem I see with "Brian and Co.'s" approach is they're layering a functional bit onto a language that is all about mutable state. What we end up with is a language that is neither safe to use in a parallel/functional/multi-core environment nor as expressive as it could be in a mutable-state environment. I'd rather see the issue of introducing "pure" functions tackled separately from the issue of lexical closure in lambda expressions. I think we could do that, and it doesn't feel like an "either/or" situation to me. -- Jim On Sun, Nov 21, 2010 at 8:45 PM, Howard Lovatt wrote: > I think Brian and Co. have got this one right; the future is multicore > and parallel processing, no point introducing something that will be > an ever increasing pain point. We should be looking to our parallel > future, not our serial past. > > I should state my bias, in that I like a functional approach and I am > much more influenced by the likes of Haskell than Lisp. I also find > the argument that you can't have closures without writing to variables > strange since languages like Haskell are normally treated as having > closures but don't have write access. Also in Church's Lambda calculus > there was no write at all! > > > ---------- Forwarded message ---------- > > From: Mikael Grev > > To: lambda-dev at openjdk.java.net > > Date: Sun, 21 Nov 2010 12:39:03 +0100 > > Subject: Re: capturing (or not) mutable local variables > > Excellent Brian. > > > > It would mean that a single compiler error would be shown when this is > broken: > > > > - A variable cannot be changed after it has been used in a closure > initialization. > > > > Easy, clear and user friendly. No need for funky hacks in the non-mutable > case. > > > > This kind of capture will not be seen as limited as the current > effectively-final definition, which only real merit is that it saves six > keystrokes. > > > > > > And I think there's a big opportunity here as well. The spin doctor in me > say that if it is carefully communicated by Oracle that 2) is a very > conscious decision and that the way you should do capture of mutable > variables is by using an AtomicXxx I think you can almost totally get away > with it. Maybe even get out on top of other languages since this is very > multi threading aware from the start. > > It is very important that all code examples for Lamdas have at least one > example of a good use of AtomicXxx for mutable variables. Then this becomes > the standard way of implementing mutability in parallel closures. There > might even be room for some syntax sweetening of AtomicXxx usage in the > future, which makes this even more compelling. > > > > Cheers, > > Mikael > > > > > > On Nov 20, 2010, at 21:41 PM, Brian Goetz wrote: > > > >> (2) is an interesting idea and worth considering. > >> > >> On Nov 20, 2010, at 5:37 AM, Mikael Grev wrote: > >> > >>> Please don't make it possible to mutate the variable from within the > closure. > >>> > >>> > >>> However, I wouldn't mind if "effectively-final" was extended so that > either: > >>> > >>> 1) The closure works on a copy, giving no restrictions on the > initialization and reassignment of the captured variable > >>> 2) Any variable is effectively-final as long as it isn't reassigned > after the closure initialization. (reordering should be solvable) > >>> > >>> The reason for this is that the following isn't that uncommon and "i" > needs to be reassigned as another variable to be effectively-final (ugly): > >>> > >>> int i = 0; > >>> if (something) > >>> i = xxx; > >>> > >>> capture of i > >>> > >>> Loosening the definition of effectively-final would effectively make > the feature easier to use. > >>> > >>> Cheers, > >>> Mikael > >>> > >>> > >>> On Nov 20, 2010, at 10:29 AM, Brian Goetz wrote: > >>> > >>>>> Hi Brian, I came to you yesterday at devoxx to ask about the > rationale > >>>>> behind the decision to not capture mutable local variables. > >>>> > >>>> I admire your passion and your persistence :) > >>>> > >>>>> I understand that among several arguments, there is a strong one > about > >>>>> preventing people from doing unsafe things. you gave the analogy of > >>>>> the kid, the fence and the pool. > >>>> > >>>> Safety mechanisms do not need to be perfect to be useful. Your > arguments seem to center around "I can climb the fence, so there's no point > in having it." This argument is, to be blunt, silly. The design principles > of the Java language stress safety, even if there are cases where such > safety guards can be "casted away" by a sufficiently motivated user. > >>>> > >>>> If you want to capture a not-effectively-final local variable in a > closure, then one of the following two things has to happen: > >>>> a) The lifetime of the variable must be extended to the lifetime of > the closure > >>>> b) The lifetime of the closure must be shortened to the lifetime of > the variable. > >>>> > >>>> In both cases, we would want to ensure the variable is only accessed > from the thread capturing the closure. In (a), we would be effectively > creating a whole new class of variables (in addition to the seven (or eight, > depending on how you count) already defined by the JLS); these are no longer > local variables and should not look like them. In both (a) and (b), we > would be creating a kind of restricted closure, whose execution is > restricted in space (confined to a specific thread) and/or time (confined to > the lifetime of the scope in which the variable is declared.) For > concreteness let's call these confined variables and confined lambdas. > >>>> > >>>> It is possible to create mechanisms to reify and enforce these various > types of confinement (we've discussed them extensively internally.) > However, they add nontrivial complexity to the language. > >>>> > >>>> In order to justify the complexity that these new features would > generate, there needs to be a compelling use case. When I explored this > issue, I asked a number of people to write down a use case for this. Every > one wrote some form of: > >>>> > >>>> int sum = 0; > >>>> list.forEach( #{ x -> sum += x.foo() } > >>>> > >>>> In a parellel world, this idiom is irretrievably broken. (See Guy > Steele's presentation "Organizing Functional Code for Parallel Execution, > or, foldl considered slightly harmful.") People will do this, they will do > this without thinking, and their code will be broken. It is very hard, even > for experts, to get this right. While Java has plenty of other > opportunities to create non-thread-safe code, I am not going to create an > entirely new and nearly irresistible vector for doing so. Iteration and > side-effects are how we've been trained to do things in Java, but we have to > learn to do better. > >>>> > >>>> In sum, adding this feature that you (and others) want so badly seems > to add up to: > >>>> - Lots of new complexity (e.g., confined variables and lambdas) > >>>> - Nearly irresistible new areas for making errors > >>>> - All to prop up a broken programming model (iterations + side > effects) > >>>> > >>>> To quote a past president: "Wouldn't be prudent." At this point in > time, it feels the risk and complexity outweighs the benefit. I would > rather put the effort into supporting map/reduce-y idioms in the libraries. > The above block is much better as: > >>>> > >>>> list.reduce( #{ x, y => x+y } ) > >>>> or > >>>> list.reduce(Reducers.SUM) > >>>> or > >>>> list.sum() > >>>> > >>>>> this thought leads to another one. in your example you are assuming > >>>>> that the foreach method will get into multithreaded stuff. > >>>> > >>>> Not quite: s/will/may/ > >>>> > >>>>> your foreach impl coud say: > >>>>> > >>>>> public void forEach(@Multithreaded Foreachable block) {...} > >>>> > >>>> We explored these issues in the JSR-166 expert group several years > ago. The basic problem is that we don't have a way of reliably expressing > "reference to thread-safe object" in the type system (either statically or > dynamically), so these things revert to being documentation rather than type > assertions. > >>>> > >>>>> for that matter, you should probably name your loop method forEachMT. > >>>> > >>>> Under consideration. Though my intuition is that (a) such a > convention will be hard to stick to and (b) in five years it will probably > look silly. > >>> > >>> > > > > > > > > > > ---------- Forwarded message ---------- > > From: Neal Gafter > > To: Alex Blewitt > > Date: Sun, 21 Nov 2010 07:06:16 -0800 > > Subject: Re: capturing (or not) mutable local variables > > On Sun, Nov 21, 2010 at 1:21 AM, Alex Blewitt >wrote: > > > >> On 21 Nov 2010, at 02:08, Neal Gafter wrote: > >> > >> > The point of my question is that mutable versus immutable has nothing > to > >> do > >> > with variable lifetime. Pretending they have anything to do with each > >> other > >> > is just unnecessarily muddying the the discussion. > >> > >> Except that this assumes it is the same variable inside and outside the > >> lambda. If it is semantically a different variable (with the lifetime of > the > >> closure) which has been assigned a copy of the value from the enclosing > >> scope then mutability, or lack thereof, is key. > >> > > > > Is it semantically different? It appears in all ways to be a single > > variable throughout its scope. The "lifetime of the variable" isn't part > of > > the semantics today, and if you distinguish one part of the variable's > scope > > from another by introducing this concept, you haven't added anything > useful > > to the description. > > > > Do you mean to intend to allow the lambda to change the value of its > copy? > > If so, your description would have some meaning. If not, you're > suggesting > > a difference without a distinctions. > > > > > > > > ---------- Forwarded message ---------- > > From: Brian Goetz > > To: Mikael Grev > > Date: Sun, 21 Nov 2010 10:50:50 -0500 > > Subject: Re: capturing (or not) mutable local variables > > You can think of effectively final as a form of type inference. There > are lots of aspects of type inference coming in Java (diamond from Coin, > target typing for lambda formals) and inferring finality is yet another. > > > > On 11/21/2010 6:39 AM, Mikael Grev wrote: > >> > >> Excellent Brian. > >> > >> It would mean that a single compiler error would be shown when this is > broken: > >> > >> - A variable cannot be changed after it has been used in a closure > initialization. > >> > >> Easy, clear and user friendly. No need for funky hacks in the > non-mutable case. > >> > >> This kind of capture will not be seen as limited as the current > effectively-final definition, which only real merit is that it saves six > keystrokes. > >> > >> > >> And I think there's a big opportunity here as well. The spin doctor in > me say that if it is carefully communicated by Oracle that 2) is a very > conscious decision and that the way you should do capture of mutable > variables is by using an AtomicXxx I think you can almost totally get away > with it. Maybe even get out on top of other languages since this is very > multi threading aware from the start. > >> It is very important that all code examples for Lamdas have at least one > example of a good use of AtomicXxx for mutable variables. Then this becomes > the standard way of implementing mutability in parallel closures. There > might even be room for some syntax sweetening of AtomicXxx usage in the > future, which makes this even more compelling. > >> > >> Cheers, > >> Mikael > >> > >> > >> On Nov 20, 2010, at 21:41 PM, Brian Goetz wrote: > >> > >>> (2) is an interesting idea and worth considering. > >>> > >>> On Nov 20, 2010, at 5:37 AM, Mikael Grev wrote: > >>> > >>>> Please don't make it possible to mutate the variable from within the > closure. > >>>> > >>>> > >>>> However, I wouldn't mind if "effectively-final" was extended so that > either: > >>>> > >>>> 1) The closure works on a copy, giving no restrictions on the > initialization and reassignment of the captured variable > >>>> 2) Any variable is effectively-final as long as it isn't reassigned > after the closure initialization. (reordering should be solvable) > >>>> > >>>> The reason for this is that the following isn't that uncommon and "i" > needs to be reassigned as another variable to be effectively-final (ugly): > >>>> > >>>> int i = 0; > >>>> if (something) > >>>> i = xxx; > >>>> > >>>> capture of i > >>>> > >>>> Loosening the definition of effectively-final would effectively make > the feature easier to use. > >>>> > >>>> Cheers, > >>>> Mikael > >>>> > >>>> > >>>> On Nov 20, 2010, at 10:29 AM, Brian Goetz wrote: > >>>> > >>>>>> Hi Brian, I came to you yesterday at devoxx to ask about the > rationale > >>>>>> behind the decision to not capture mutable local variables. > >>>>> > >>>>> I admire your passion and your persistence :) > >>>>> > >>>>>> I understand that among several arguments, there is a strong one > about > >>>>>> preventing people from doing unsafe things. you gave the analogy of > >>>>>> the kid, the fence and the pool. > >>>>> > >>>>> Safety mechanisms do not need to be perfect to be useful. Your > arguments seem to center around "I can climb the fence, so there's no point > in having it." This argument is, to be blunt, silly. The design principles > of the Java language stress safety, even if there are cases where such > safety guards can be "casted away" by a sufficiently motivated user. > >>>>> > >>>>> If you want to capture a not-effectively-final local variable in a > closure, then one of the following two things has to happen: > >>>>> a) The lifetime of the variable must be extended to the lifetime of > the closure > >>>>> b) The lifetime of the closure must be shortened to the lifetime of > the variable. > >>>>> > >>>>> In both cases, we would want to ensure the variable is only accessed > from the thread capturing the closure. In (a), we would be effectively > creating a whole new class of variables (in addition to the seven (or eight, > depending on how you count) already defined by the JLS); these are no longer > local variables and should not look like them. In both (a) and (b), we > would be creating a kind of restricted closure, whose execution is > restricted in space (confined to a specific thread) and/or time (confined to > the lifetime of the scope in which the variable is declared.) For > concreteness let's call these confined variables and confined lambdas. > >>>>> > >>>>> It is possible to create mechanisms to reify and enforce these > various types of confinement (we've discussed them extensively internally.) > However, they add nontrivial complexity to the language. > >>>>> > >>>>> In order to justify the complexity that these new features would > generate, there needs to be a compelling use case. When I explored this > issue, I asked a number of people to write down a use case for this. Every > one wrote some form of: > >>>>> > >>>>> int sum = 0; > >>>>> list.forEach( #{ x -> sum += x.foo() } > >>>>> > >>>>> In a parellel world, this idiom is irretrievably broken. (See Guy > Steele's presentation "Organizing Functional Code for Parallel Execution, > or, foldl considered slightly harmful.") People will do this, they will do > this without thinking, and their code will be broken. It is very hard, even > for experts, to get this right. While Java has plenty of other > opportunities to create non-thread-safe code, I am not going to create an > entirely new and nearly irresistible vector for doing so. Iteration and > side-effects are how we've been trained to do things in Java, but we have to > learn to do better. > >>>>> > >>>>> In sum, adding this feature that you (and others) want so badly seems > to add up to: > >>>>> - Lots of new complexity (e.g., confined variables and lambdas) > >>>>> - Nearly irresistible new areas for making errors > >>>>> - All to prop up a broken programming model (iterations + side > effects) > >>>>> > >>>>> To quote a past president: "Wouldn't be prudent." At this point in > time, it feels the risk and complexity outweighs the benefit. I would > rather put the effort into supporting map/reduce-y idioms in the libraries. > The above block is much better as: > >>>>> > >>>>> list.reduce( #{ x, y => x+y } ) > >>>>> or > >>>>> list.reduce(Reducers.SUM) > >>>>> or > >>>>> list.sum() > >>>>> > >>>>>> this thought leads to another one. in your example you are assuming > >>>>>> that the foreach method will get into multithreaded stuff. > >>>>> > >>>>> Not quite: s/will/may/ > >>>>> > >>>>>> your foreach impl coud say: > >>>>>> > >>>>>> public void forEach(@Multithreaded Foreachable block) {...} > >>>>> > >>>>> We explored these issues in the JSR-166 expert group several years > ago. The basic problem is that we don't have a way of reliably expressing > "reference to thread-safe object" in the type system (either statically or > dynamically), so these things revert to being documentation rather than type > assertions. > >>>>> > >>>>>> for that matter, you should probably name your loop method > forEachMT. > >>>>> > >>>>> Under consideration. Though my intuition is that (a) such a > convention will be hard to stick to and (b) in five years it will probably > look silly. > >>>> > >>>> > >> > >> > > > > > > > > > > -- > -- Howard. > > From isidore at setgame.com Sun Nov 21 22:39:48 2010 From: isidore at setgame.com (Llewellyn Falco) Date: Sun, 21 Nov 2010 22:39:48 -0800 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: I also want to point out that there seems to be an assumption that the consumer of the lambda has ANY idea that creator of the lambda USED a lambda. Remember that the signature for someone using a lambda will be like public List where(Func f) The fact that you created the Func SAM interface with a lambda is not known to the user of the lambda. Therefore, the idea that some of the creations might be thread safe, isn't very useful. Llewellyn From howard.lovatt at gmail.com Sun Nov 21 23:57:44 2010 From: howard.lovatt at gmail.com (Howard Lovatt) Date: Mon, 22 Nov 2010 18:57:44 +1100 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: @Jim & Llewellyn, You raise a number of points: 1. To me the proposed lambdas are consistent with Java, in particular with inner classes (which also only close over finals). The type of the lambda must be a SAM, which means it could be an instance of a class (including inner class) or a lambda, therefore it is important that something written to work with an inner class works with a lambda. 2. As others have pointed out fields can be marked volatile and locals can't, which helps in making a lambda thread safe when writing to a field (and it would be hard to add volatile to a local). 3. In Haskell a closure that interacts with mutable state via a monad can't, in general, be passed to a non-monodic function or vice versa (and this can be a pain and is an active area of research in the Haskell community to remove this restriction). In the proposed lambda model you can pass the lambdas freely to anything, that is a nice feature (you don't need to know the details of the function you call). 4. I agree that Java isn't the ideal parallel language, but lets learn from the mistakes of others and move away from mutable state and set Java up as better than C#, Scala, Lisp, etc. in the parallel domain. Tell people that if they want mutable state they should use AtomicXXX to guarantee thread safety. It is a pity that Java didn't make everything immutable in 95 and that you have to mark stuff as mutable using AtomicXXX (however this is looking back 15 years and it is very easy to spot mistakes in hindsight and the fact is that Java was much better than anything else in 95 and did provide state of the art parallel constructs for 95). Cheers, -- Howard. On 22 November 2010 17:39, Llewellyn Falco wrote: > I also want to point out that there seems to be an?assumption that the > consumer of the lambda has ANY idea that creator of the lambda USED a > lambda. > Remember that the signature for someone using a lambda will be like > > public List where(Func f) > > The fact that you created the Func SAM interface with a lambda is not known > to the user of the lambda. > Therefore, the idea that some of the?creations?might be thread safe, isn't > very useful. > > Llewellyn -- ? -- Howard. From forax at univ-mlv.fr Mon Nov 22 00:17:18 2010 From: forax at univ-mlv.fr (=?ISO-8859-1?Q?R=E9mi_Forax?=) Date: Mon, 22 Nov 2010 09:17:18 +0100 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: <4CEA270E.3050007@univ-mlv.fr> Le 22/11/2010 08:57, Howard Lovatt a ?crit : [...] > Tell people that if they want mutable state they should use AtomicXXX > to guarantee thread safety. It is a pity that Java didn't make Using AtomicXXX is not a good idea too. By example, if you want to sum a collection of integers using an atomic variable, this means the volatile variable (inside the atomic class) will be updated for each element. If you use a map/reduce pattern, you only need one synchronization point at the end of the calculation so the map/reduce code outperforms the solution with an atomic variable. > everything immutable in 95 and that you have to mark stuff as mutable > using AtomicXXX (however this is looking back 15 years and it is very > easy to spot mistakes in hindsight and the fact is that Java was much > better than anything else in 95 and did provide state of the art > parallel constructs for 95). > > Cheers, > > -- Howard. R?mi From schmidtfx at googlemail.com Mon Nov 22 01:13:37 2010 From: schmidtfx at googlemail.com (Felix Schmidt) Date: Mon, 22 Nov 2010 10:13:37 +0100 Subject: Compile Message-ID: <-5136260800772443964@unknownmsgid> I think this is a common question: how could I compile a simple example with closures from the lambda project? I have installed the standard compiler of open jdk 7. Thank you felix From maurizio.cimadamore at oracle.com Mon Nov 22 01:30:19 2010 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 22 Nov 2010 09:30:19 +0000 Subject: Compile In-Reply-To: <-5136260800772443964@unknownmsgid> References: <-5136260800772443964@unknownmsgid> Message-ID: <4CEA382B.8090609@oracle.com> On 22/11/10 09:13, Felix Schmidt wrote: > I think this is a common question: how could I compile a simple > example with closures from the lambda project? > > I have installed the standard compiler of open jdk 7. > > Thank you > felix > > The instruction in this email [1] should help. Feel free to follow up if you have specific questions. [1] - http://mail.openjdk.java.net/pipermail/lambda-dev/2010-August/002179.html Thanks Maurizio From ss at comp.lancs.ac.uk Mon Nov 22 03:25:24 2010 From: ss at comp.lancs.ac.uk (Steven Simpson) Date: Mon, 22 Nov 2010 11:25:24 +0000 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: <4CEA5324.2040807@comp.lancs.ac.uk> On 22/11/10 07:57, Howard Lovatt wrote: > 2. As others have pointed out fields can be marked volatile and locals > can't, which helps in making a lambda thread safe when writing to a > field (and it would be hard to add volatile to a local). Would it be so hard? 'volatile' is currently meaningless on a local because it can only be accessed by one thread anyway, so it would be safe to ignore it if not referenced from a closure/inner class (right?). If it was referenced from a closure or inner class, how would it be made mutable? From what I've seen, the only proposals are to box it up in some object, e.g. array of 1, or anonymous class. Within that box, it could be declared volatile. Cheers! -- From alessiostalla at gmail.com Mon Nov 22 04:21:12 2010 From: alessiostalla at gmail.com (Alessio Stalla) Date: Mon, 22 Nov 2010 13:21:12 +0100 Subject: capturing (or not) mutable local variables In-Reply-To: <4CEA270E.3050007@univ-mlv.fr> References: <4CEA270E.3050007@univ-mlv.fr> Message-ID: On Mon, Nov 22, 2010 at 9:17 AM, R?mi Forax wrote: > ?Le 22/11/2010 08:57, Howard Lovatt a ?crit : > > [...] > >> Tell people that if they want mutable state they should use AtomicXXX >> to guarantee thread safety. It is a pity that Java didn't make > > Using AtomicXXX is not a good idea too. > > By example, if you want to sum a collection of integers using an atomic > variable, this means the volatile variable (inside the atomic class) will be updated > for each element. > > If you use a map/reduce pattern, you only need one synchronization point > at the end of the calculation so the map/reduce code outperforms > the solution with an atomic variable. I'd like to point out that, in my experience with Lisp, the mutability of the closed-over variables is rarely if ever used to update a local to be used further down the same function/method; in other words, int i = 0; foo.foreach(#{ x -> i += x; }); use(i); is not a common code pattern, and it's generally considered bad style. Instead, mutable captured variables are used much more often as a way to give some private state to a closure, effectively turning it into a lightweight object (in the OO sense). So, while I do believe that "true" closures should close over their lexical environment without restrictions, including mutability, I think in Java the issue isn't/won't be that important; if you want private state you can still use a plain old inner class and explicitly capture (copy) the locals you're interested in - after all, closures are going to be "syntax sugar" for SAM types anyway - or you can use final/effectively final (+ maybe AtomicXxx if you need it). From peter.levart at marand.si Mon Nov 22 04:38:27 2010 From: peter.levart at marand.si (Peter Levart) Date: Mon, 22 Nov 2010 13:38:27 +0100 Subject: capturing (or not) mutable local variables In-Reply-To: <4CEA270E.3050007@univ-mlv.fr> References: <4CEA270E.3050007@univ-mlv.fr> Message-ID: <201011221338.27217.peter.levart@marand.si> On 11/22/10, R?mi Forax wrote: > Using AtomicXXX is not a good idea too. > > By example, if you want to sum a collection of integers using an atomic > variable, > this means the volatile variable (inside the atomic class) will be updated > for each element. > > If you use a map/reduce pattern, you only need one synchronization point > at the end of the calculation so the map/reduce code outperforms > the solution with an atomic variable. Or you can use Cliff Click's ConcurrentAutoTable from the http://sourceforge.net/projects/high-scale-lib/ Which is a single counter that automatically spreads the load (using hashing) to multiple independent counters and at each retrieval sums (reduces) them. The style you take to solve a problem depends on the problem. map/reduce is suitable for batch processing while a single counter is more suitable if you want concurrent monitoring of dynamic counter. Regards, Peter From opinali at gmail.com Mon Nov 22 05:35:41 2010 From: opinali at gmail.com (Osvaldo Pinali Doederlein) Date: Mon, 22 Nov 2010 08:35:41 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: References: <4CEA270E.3050007@univ-mlv.fr> Message-ID: <4CEA71AD.7040404@gmail.com> On 22/11/2010 07:21, Alessio Stalla wrote: > I'd like to point out that, in my experience with Lisp, the mutability > of the closed-over variables is rarely if ever used to update a local > to be used further down the same function/method; in other words, No much Lisp here, but in other langs like Smalltalk, full closures are essential for user/library-defined control structures. That is the big door that's being closed by the current closures spec; but we're already past this discussion, we're not going to have any special syntax for control abstractions. Still, I see this limitation as extra, artificial, unnecessary complexity. On the concurrency argument, I'm with Pavel and others - like it or not the Java language is deeply rooted in the "serial past", mutability is its main paradigm, and it's probably impossible to really fix that (even to reach a decent hybrid-paradigm language) while maintaining backwards compatibility. The current closures spec will make things worse for serial code, while not necessarily making anything better for concurrent code. That could be different if Java8 had a broader revision towards functional behavior - for example, extra syntax like const qualifiers, inference to detect immutability and function pureness etc. (The late JavaFX Script was already making some nice progress in these directions, hopefully this will continue in Visage and other Java-like JVM langs). But such revision is not in the plans for Java8 (although the Checker Framework is). A+ Osvaldo > int i = 0; > foo.foreach(#{ x -> i += x; }); > use(i); > > is not a common code pattern, and it's generally considered bad style. > Instead, mutable captured variables are used much more often as a way > to give some private state to a closure, effectively turning it into a > lightweight object (in the OO sense). So, while I do believe that > "true" closures should close over their lexical environment without > restrictions, including mutability, I think in Java the issue > isn't/won't be that important; if you want private state you can still > use a plain old inner class and explicitly capture (copy) the locals > you're interested in - after all, closures are going to be "syntax > sugar" for SAM types anyway - or you can use final/effectively final > (+ maybe AtomicXxx if you need it). > From per at bothner.com Mon Nov 22 11:18:41 2010 From: per at bothner.com (Per Bothner) Date: Mon, 22 Nov 2010 11:18:41 -0800 Subject: capturing (or not) mutable local variables In-Reply-To: <4CEA5324.2040807@comp.lancs.ac.uk> References: <4CEA5324.2040807@comp.lancs.ac.uk> Message-ID: <4CEAC211.50205@bothner.com> On 11/22/2010 03:25 AM, Steven Simpson wrote: > If it was referenced from a closure or inner class, how would it be made > mutable? From what I've seen, the only proposals are to box it up in > some object, e.g. array of 1, or anonymous class. Within that box, it > could be declared volatile. If a lambda is implemented as an inner class, then any captured mutable local variables can be implemented as fields of the closure class. I.e. you don't need to allocate any extra objects or create new classes. That means all accesses to the variable (even the ones outside the closure) get translated to access of the field in the closure. But, you say, the variable is defined earlier than the closure, and possibly in an outer scope! The solution: Pre-allocate the closure when entering the scope of the variable. It gets more complicated if the closure captures mutable variable from multiple different scopes, especially if the inner scope is in a loop - in that case, I think you do have to allocate a helper object. I did this for Kawa *before* Java had inner classes - i.e. in the jdk-1.0 dawn of Java. Look for "Old closure implementation" in: http://www.gnu.org/software/kawa/internals/procedures.html -- --Per Bothner per at bothner.com http://per.bothner.com/ From schmidtfx at googlemail.com Mon Nov 22 12:49:44 2010 From: schmidtfx at googlemail.com (Felix Schmidt) Date: Mon, 22 Nov 2010 21:49:44 +0100 Subject: Compile In-Reply-To: <4CEA382B.8090609@oracle.com> References: <-5136260800772443964@unknownmsgid> <4CEA382B.8090609@oracle.com> Message-ID: <4CEAD768.4060608@googlemail.com> Maurizio, thank you for your reply. I compiled the langtools :) But how can I compile my application with the closures feature? langtools/dist/bin/javac CTest.java ? Thank you felix On 11/22/2010 10:30 AM, Maurizio Cimadamore wrote: > On 22/11/10 09:13, Felix Schmidt wrote: >> I think this is a common question: how could I compile a simple >> example with closures from the lambda project? >> >> I have installed the standard compiler of open jdk 7. >> >> Thank you >> felix >> > The instruction in this email [1] should help. Feel free to follow up > if you have specific questions. > > [1] - > http://mail.openjdk.java.net/pipermail/lambda-dev/2010-August/002179.html > > Thanks > Maurizio From forax at univ-mlv.fr Mon Nov 22 13:46:13 2010 From: forax at univ-mlv.fr (=?ISO-8859-1?Q?R=E9mi_Forax?=) Date: Mon, 22 Nov 2010 22:46:13 +0100 Subject: Compile In-Reply-To: <4CEAD768.4060608@googlemail.com> References: <-5136260800772443964@unknownmsgid> <4CEA382B.8090609@oracle.com> <4CEAD768.4060608@googlemail.com> Message-ID: <4CEAE4A5.2010808@univ-mlv.fr> On 11/22/2010 09:49 PM, Felix Schmidt wrote: > Maurizio, > > thank you for your reply. I compiled the langtools :) > > But how can I compile my application with the closures feature? > > langtools/dist/bin/javac CTest.java ? > > Thank you > felix java -cp classes.jar:. com.sun.tools.javac.Main CTest.java R?mi From schmidtfx at googlemail.com Mon Nov 22 14:03:52 2010 From: schmidtfx at googlemail.com (Felix Schmidt) Date: Mon, 22 Nov 2010 23:03:52 +0100 Subject: Compile In-Reply-To: <4CEAE4A5.2010808@univ-mlv.fr> References: <-5136260800772443964@unknownmsgid> <4CEA382B.8090609@oracle.com> <4CEAD768.4060608@googlemail.com> <4CEAE4A5.2010808@univ-mlv.fr> Message-ID: <4CEAE8C8.1040403@googlemail.com> I got the following error CTest.java:4: '{' expected #int() fortyTwo = #()(42); ^ CTest.java:4: expected #int() fortyTwo = #()(42); ^ CTest.java:4: ';' expected #int() fortyTwo = #()(42); ^ CTest.java:4: '{' expected #int() fortyTwo = #()(42); ^ CTest.java:4: ';' expected #int() fortyTwo = #()(42); ^ CTest.java:4: not a statement #int() fortyTwo = #()(42); ^ 6 errors On 11/22/2010 10:46 PM, R?mi Forax wrote: > On 11/22/2010 09:49 PM, Felix Schmidt wrote: > >> Maurizio, >> >> thank you for your reply. I compiled the langtools :) >> >> But how can I compile my application with the closures feature? >> >> langtools/dist/bin/javac CTest.java ? >> >> Thank you >> felix >> > java -cp classes.jar:. com.sun.tools.javac.Main CTest.java > > R?mi > > > From jim at pentastich.org Mon Nov 22 14:25:17 2010 From: jim at pentastich.org (Jim Mayer) Date: Mon, 22 Nov 2010 17:25:17 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: @Howard, I think that different people are coming from different places. It does seem pretty clear where Oracle wants to go with this. In my case, the processors I'm deploying on are in (large) embedded systems and are all single-core today and the calculations we're doing are non-numeric. There's a large UI component to what we do as well, so much of what we do HAS to be on a single thread. I'm interested in Java mostly because it is "clean enough" so that high quality development environments are available for it. Working in Java is vastly more effective for us than working in C++, and the bulk of that comes (I think) from the development environment. Unfortunately, Java is also resource hog. Much of that comes from the design of the libraries and of class overhead. Class overhead isn't a big deal for classes that are large or are used in many places, but it is significant for anonymous inner classes and, probably, for the lambda-like objects being discussed here. One of the things I was hoping for from the closures effort was a closure object that was much lighter weight than an anonymous inner class. If you don't believe that this is significant, then I suggest that you look at the reflexion heavy contortions that folks doing data binding (JSR 295 and similar approaches) go through to avoid it (e.g., 'BeanProperty.create("propertyName")'). I know the issue has been discussed extensively (e.g., http://java.sun.com/docs/white/delegates.html), but it's not clear to me that anything much has happened in that area. Finally, features that rely on a VM as sophisticated as Hotspot for adequate performance are a real problem in my application space. Although hotspot JVMs have a big footprint, the real issue is that they're not even available for the processors we use. Shark may get there someday, but it's not stable now. As for the points you raised: (1) I agree that it's compatible with what Java does now. That limitation was one of the first things I noticed when anonymous inner classes came along. I remember being rather surprised at the time, since Sun had lots of people who were well aware of what other languages had (successfully) done with similar features. (2) Well, volatile doesn't really help with read/write thread safety issues anyway. A "safe" read followed by by a "safe" write still leaves an unsafe transaction. (3) If the goal is to be able to pass lambda expressions to APIs without knowing whether the API could use parallel execution then I think we'll be very disappointed. Given the mutable state history of Java, APIs will need to be explicit about what they plan to do with closures/interfaces/etc. that they are passed. (4) I guess I still don't see the problem with marking API entries with "@Pure" annotations (or something like that), and letting the compiler generate warnings for problematic usage. It's not necessary for the warnings to be complete for them to be useful. Anyway, that's the background I've been coming from. Cheers! -- Jim On Mon, Nov 22, 2010 at 2:57 AM, Howard Lovatt wrote: > @Jim & Llewellyn, > > You raise a number of points: > > 1. To me the proposed lambdas are consistent with Java, in particular > with inner classes (which also only close over finals). The type of > the lambda must be a SAM, which means it could be an instance of a > class (including inner class) or a lambda, therefore it is important > that something written to work with an inner class works with a > lambda. > > 2. As others have pointed out fields can be marked volatile and locals > can't, which helps in making a lambda thread safe when writing to a > field (and it would be hard to add volatile to a local). > > 3. In Haskell a closure that interacts with mutable state via a monad > can't, in general, be passed to a non-monodic function or vice versa > (and this can be a pain and is an active area of research in the > Haskell community to remove this restriction). In the proposed lambda > model you can pass the lambdas freely to anything, that is a nice > feature (you don't need to know the details of the function you call). > > 4. I agree that Java isn't the ideal parallel language, but lets learn > from the mistakes of others and move away from mutable state and set > Java up as better than C#, Scala, Lisp, etc. in the parallel domain. > Tell people that if they want mutable state they should use AtomicXXX > to guarantee thread safety. It is a pity that Java didn't make > everything immutable in 95 and that you have to mark stuff as mutable > using AtomicXXX (however this is looking back 15 years and it is very > easy to spot mistakes in hindsight and the fact is that Java was much > better than anything else in 95 and did provide state of the art > parallel constructs for 95). > > Cheers, > > -- Howard. > > On 22 November 2010 17:39, Llewellyn Falco wrote: > > I also want to point out that there seems to be an assumption that the > > consumer of the lambda has ANY idea that creator of the lambda USED a > > lambda. > > Remember that the signature for someone using a lambda will be like > > > > public List where(Func f) > > > > The fact that you created the Func SAM interface with a lambda is not > known > > to the user of the lambda. > > Therefore, the idea that some of the creations might be thread safe, > isn't > > very useful. > > > > Llewellyn > > > > -- > -- Howard. > From forax at univ-mlv.fr Mon Nov 22 14:26:00 2010 From: forax at univ-mlv.fr (=?ISO-8859-1?Q?R=E9mi_Forax?=) Date: Mon, 22 Nov 2010 23:26:00 +0100 Subject: Compile In-Reply-To: <4CEAE8C8.1040403@googlemail.com> References: <-5136260800772443964@unknownmsgid> <4CEA382B.8090609@oracle.com> <4CEAD768.4060608@googlemail.com> <4CEAE4A5.2010808@univ-mlv.fr> <4CEAE8C8.1040403@googlemail.com> Message-ID: <4CEAEDF8.1090208@univ-mlv.fr> On 11/22/2010 11:03 PM, Felix Schmidt wrote: > I got the following error > > CTest.java:4: '{' expected > #int() fortyTwo = #()(42); > ^ > CTest.java:4: expected > #int() fortyTwo = #()(42); > ^ > CTest.java:4: ';' expected > #int() fortyTwo = #()(42); > ^ > CTest.java:4: '{' expected > #int() fortyTwo = #()(42); > ^ > CTest.java:4: ';' expected > #int() fortyTwo = #()(42); > ^ > CTest.java:4: not a statement > #int() fortyTwo = #()(42); > ^ > 6 errors You're using an old syntax. Here is the last state of the lambda spec: http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-3.html R?mi From markmahieu at gmail.com Mon Nov 22 14:29:40 2010 From: markmahieu at gmail.com (Mark Mahieu) Date: Mon, 22 Nov 2010 22:29:40 +0000 Subject: Compile In-Reply-To: <4CEAE8C8.1040403@googlemail.com> References: <-5136260800772443964@unknownmsgid> <4CEA382B.8090609@oracle.com> <4CEAD768.4060608@googlemail.com> <4CEAE4A5.2010808@univ-mlv.fr> <4CEAE8C8.1040403@googlemail.com> Message-ID: <12F92130-501D-4A66-A47F-F170FB3869C7@gmail.com> Hi Felix, Your test uses an older syntax (and features). For current documents and examples, see: http://mail.openjdk.java.net/pipermail/lambda-dev/2010-October/002476.html and the attachment to: http://mail.openjdk.java.net/pipermail/lambda-dev/2010-October/002543.html Regards, Mark On 22 Nov 2010, at 22:03, Felix Schmidt wrote: > I got the following error > > CTest.java:4: '{' expected > #int() fortyTwo = #()(42); > ^ > CTest.java:4: expected > #int() fortyTwo = #()(42); > ^ > CTest.java:4: ';' expected > #int() fortyTwo = #()(42); > ^ > CTest.java:4: '{' expected > #int() fortyTwo = #()(42); > ^ > CTest.java:4: ';' expected > #int() fortyTwo = #()(42); > ^ > CTest.java:4: not a statement > #int() fortyTwo = #()(42); > ^ > 6 errors > > > > On 11/22/2010 10:46 PM, R?mi Forax wrote: >> On 11/22/2010 09:49 PM, Felix Schmidt wrote: >> >>> Maurizio, >>> >>> thank you for your reply. I compiled the langtools :) >>> >>> But how can I compile my application with the closures feature? >>> >>> langtools/dist/bin/javac CTest.java ? >>> >>> Thank you >>> felix >>> >> java -cp classes.jar:. com.sun.tools.javac.Main CTest.java >> >> R?mi >> >> >> > > From schmidtfx at googlemail.com Mon Nov 22 15:06:58 2010 From: schmidtfx at googlemail.com (Felix Schmidt) Date: Tue, 23 Nov 2010 00:06:58 +0100 Subject: Compile In-Reply-To: <12F92130-501D-4A66-A47F-F170FB3869C7@gmail.com> References: <-5136260800772443964@unknownmsgid> <4CEA382B.8090609@oracle.com> <4CEAD768.4060608@googlemail.com> <4CEAE4A5.2010808@univ-mlv.fr> <4CEAE8C8.1040403@googlemail.com> <12F92130-501D-4A66-A47F-F170FB3869C7@gmail.com> Message-ID: <4CEAF792.2070707@googlemail.com> Thank you everybody, got it working :) felix On 11/22/2010 11:29 PM, Mark Mahieu wrote: > Hi Felix, > > Your test uses an older syntax (and features). For current documents > and examples, see: > > http://mail.openjdk.java.net/pipermail/lambda-dev/2010-October/002476.html > > and the attachment to: > > http://mail.openjdk.java.net/pipermail/lambda-dev/2010-October/002543.html > > Regards, > > Mark > > > On 22 Nov 2010, at 22:03, Felix Schmidt wrote: > >> I got the following error >> >> CTest.java:4: '{' expected >> #int() fortyTwo = #()(42); >> ^ >> CTest.java:4: expected >> #int() fortyTwo = #()(42); >> ^ >> CTest.java:4: ';' expected >> #int() fortyTwo = #()(42); >> ^ >> CTest.java:4: '{' expected >> #int() fortyTwo = #()(42); >> ^ >> CTest.java:4: ';' expected >> #int() fortyTwo = #()(42); >> ^ >> CTest.java:4: not a statement >> #int() fortyTwo = #()(42); >> ^ >> 6 errors >> >> >> >> On 11/22/2010 10:46 PM, R?mi Forax wrote: >>> On 11/22/2010 09:49 PM, Felix Schmidt wrote: >>> >>>> Maurizio, >>>> >>>> thank you for your reply. I compiled the langtools :) >>>> >>>> But how can I compile my application with the closures feature? >>>> >>>> langtools/dist/bin/javac CTest.java ? >>>> >>>> Thank you >>>> felix >>>> >>> java -cp classes.jar:. com.sun.tools.javac.Main CTest.java >>> >>> R?mi >>> >>> >>> >> >> > From vincent at sevel.eu Mon Nov 22 15:36:40 2010 From: vincent at sevel.eu (Vincent Sevel) Date: Tue, 23 Nov 2010 00:36:40 +0100 Subject: capturing (or not) mutable local variables Message-ID: Hi Brian, > Safety mechanisms do not need to be perfect to be useful. Your arguments seem to center around "I can climb the fence, so there's no point in having it." This argument is, to be blunt, silly. The design principles of the Java language stress safety, even if there are cases where such safety guards can be "casted away" by a sufficiently motivated user. here were the main ideas: the final requirement on variables - protects only a very limited number of use cases - gives a false sense of protection for all the other use cases that do require protection - is useless and counterproductive in a large number of legitimate use cases, where ugly workarounds have to be used (eg: the final array wrapper) additionally: the real issue - is not with the variable being final or not, - but with the closure not respecting the api contract in terms of multithreaded safety With all the messages going back and forth, it strikes me how much energy is put in defending the final variable, compared to the forest of all the other use cases, such as: final List square = new ArrayList(); list.forEach(#{ e -> square.add(e*e); }); How is that really different from your simple broken example? Why would we be ok with that one, but not with yours? Should not we be all equally screaming 'please do not make this non thread safe closure passable to this multithreaded method'?. > People will do this, they will do this without thinking, and their code will be broken. It is very hard, even for experts, to get this right. You realize, of course, that this exact sentence applies to the example above. >> public void forEach(@Multithreaded Foreachable block) {...} > We explored these issues in the JSR-166 expert group several years ago. The basic problem is that we don't have a way of reliably expressing "reference to thread-safe object" in the type system (either statically or dynamically), so these things revert to being documentation rather than type assertions. It is documentation that tools (like compilers) can take advantage of, so it is already much better than javadoc, and still way better than naming convention. >> for that matter, you should probably name your loop method forEachMT. > Under consideration. Though my intuition is that (a) such a convention will be hard to stick to and (b) in five years it will probably look silly. There are predecessors: SwingUtilities.invokeLater/invokeAndWait. Does it look silly? May be, but at least, while missing a mean to express thread specific constraints on a contract, we can recognize that it has some greater visibility than if it was hiding in the javadoc, I see the final requirement on variables as bureaucracy: adds little value, and mostly gets in the way of code agility. It is just a lost opportunity, but java developers have come to cope with it. However, I have battled enough with the double-checked locking anti-pattern, and unproperly synchronized code to know that whenever you provide an API that does something with threading, it needs to come with a red flag. Fix that, and you will realize that the mutable variable is not that of a problem. But may be a reality check might be helpful: send me the forEach documentation and signature, and I will ask the 25 developers in my company to tell me what is the outcome of the square example. Thanks for listening. Cheers, Vincent From brian.goetz at oracle.com Mon Nov 22 16:08:08 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 22 Nov 2010 19:08:08 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: <4CEB05E8.5000700@oracle.com> > final List square = new ArrayList(); > list.forEach(#{ e -> square.add(e*e); }); > > How is that really different from your simple broken example? Why > would we be ok with that one, but not with yours? Should not we be all > equally screaming 'please do not make this non thread safe closure > passable to this multithreaded method'?. One significant difference is that the reference-based version *can* be made thread-safe by making the referent thread-safe (say, by replacing "new ArrayList()" with "Collections.synchronizedList(new ArrayList())"). The primitive version cannot realistically be made thread-safe. (Effectively, you have provided the argument why we should not have a list.forEach() all -- it demands that the client use side-effects to achieve the desired results, which invites trouble.) In any case, I think it is time to close this discussion. You've made your point, some people agree with you, and some others disagree. But we do not intend to lift this restriction at this time, and I don't think further discussion will bring to light any issues that have not already been considered. We can always revisit this issue in a future iteration (choosing not to lift a restriction now does not foreclose on our ability to do so later). From jesse.sightler at gmail.com Mon Nov 22 16:28:33 2010 From: jesse.sightler at gmail.com (Jesse Sightler) Date: Mon, 22 Nov 2010 19:28:33 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: FWIW, I agree 100%. The thing that strikes me is that almost all of the arguments for required final would have applied equally well to required synchronization on list structures (aka Vectors) just a few years ago. Ie, some stuff is multithreaded and we want users to be safe without thinking. But that just doesn't hold up to real scrutiny IMO. On Nov 22, 2010 6:37 PM, "Vincent Sevel" wrote: > Hi Brian, > >> Safety mechanisms do not need to be perfect to be useful. Your arguments seem to center around "I can climb the fence, so there's no point in having it." This argument is, to be blunt, silly. The design principles of the Java language stress safety, even if there are cases where such safety guards can be "casted away" by a sufficiently motivated user. > > here were the main ideas: the final requirement on variables > - protects only a very limited number of use cases > - gives a false sense of protection for all the other use cases that > do require protection > - is useless and counterproductive in a large number of legitimate > use cases, where ugly workarounds have to be used (eg: the final array > wrapper) > additionally: the real issue > - is not with the variable being final or not, > - but with the closure not respecting the api contract in terms of > multithreaded safety > > With all the messages going back and forth, it strikes me how much > energy is put in defending the final variable, compared to the forest > of all the other use cases, such as: > > final List square = new ArrayList(); > list.forEach(#{ e -> square.add(e*e); }); > > How is that really different from your simple broken example? Why > would we be ok with that one, but not with yours? Should not we be all > equally screaming 'please do not make this non thread safe closure > passable to this multithreaded method'?. > >> People will do this, they will do this without thinking, and their code will be broken. It is very hard, even for experts, to get this right. > > You realize, of course, that this exact sentence applies to the example above. > >>> public void forEach(@Multithreaded Foreachable block) {...} >> We explored these issues in the JSR-166 expert group several years ago. The basic problem is that we don't have a way of reliably expressing "reference to thread-safe object" in the type system (either statically or dynamically), so these things revert to being documentation rather than type assertions. > > It is documentation that tools (like compilers) can take advantage of, > so it is already much better than javadoc, and still way better than > naming convention. > >>> for that matter, you should probably name your loop method forEachMT. >> Under consideration. Though my intuition is that (a) such a convention will be hard to stick to and (b) in five years it will probably look silly. > > There are predecessors: SwingUtilities.invokeLater/invokeAndWait. Does > it look silly? May be, but at least, while missing a mean to express > thread specific constraints on a contract, we can recognize that it > has some greater visibility than if it was hiding in the javadoc, > > > I see the final requirement on variables as bureaucracy: adds little > value, and mostly gets in the way of code agility. It is just a lost > opportunity, but java developers have come to cope with it. However, I > have battled enough with the double-checked locking anti-pattern, and > unproperly synchronized code to know that whenever you provide an API > that does something with threading, it needs to come with a red flag. > Fix that, and you will realize that the mutable variable is not that > of a problem. > > But may be a reality check might be helpful: send me the forEach > documentation and signature, and I will ask the 25 developers in my > company to tell me what is the outcome of the square example. > > Thanks for listening. Cheers, > Vincent > From jim at pentastich.org Mon Nov 22 16:28:42 2010 From: jim at pentastich.org (Jim Mayer) Date: Mon, 22 Nov 2010 19:28:42 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: <4CEB05E8.5000700@oracle.com> References: <4CEB05E8.5000700@oracle.com> Message-ID: Ended on my part... but do note that using "Collections.synchronizedList(new ArrayList())" does NOT make Vincent's example thread safe. The expectation that virtually all developers would have is that applying the example to {1,2,3,4} would be {1,4,9,16}, but with parallel execution {4,9,16,1} would be a perfectly legal outcome. Now, if his example had used a Set instead of a list THEN the adding the synchronization would have generated the expected result. -- Jim On Mon, Nov 22, 2010 at 7:08 PM, Brian Goetz wrote: > > final List square = new ArrayList(); > > list.forEach(#{ e -> square.add(e*e); }); > > > > How is that really different from your simple broken example? Why > > would we be ok with that one, but not with yours? Should not we be all > > equally screaming 'please do not make this non thread safe closure > > passable to this multithreaded method'?. > > One significant difference is that the reference-based version *can* be > made > thread-safe by making the referent thread-safe (say, by replacing "new > ArrayList()" with "Collections.synchronizedList(new ArrayList())"). The > primitive version cannot realistically be made thread-safe. > > (Effectively, you have provided the argument why we should not have a > list.forEach() all -- it demands that the client use side-effects to > achieve > the desired results, which invites trouble.) > > In any case, I think it is time to close this discussion. You've made your > point, some people agree with you, and some others disagree. But we do not > intend to lift this restriction at this time, and I don't think further > discussion will bring to light any issues that have not already been > considered. We can always revisit this issue in a future iteration > (choosing > not to lift a restriction now does not foreclose on our ability to do so > later). > > > > From reinier at zwitserloot.com Mon Nov 22 19:32:43 2010 From: reinier at zwitserloot.com (Reinier Zwitserloot) Date: Tue, 23 Nov 2010 04:32:43 +0100 Subject: capturing (or not) mutable local variables In-Reply-To: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> Message-ID: Brian's original response to Vincent (quoted below) says almost all that needs to be said here. A few add-ons: AtomicReference is the go-to utility if you must have a mutable from outer state. It would be nice if Atomic* were extended to cover all types. There's Integer, Long, and Boolean (where'd that come from?). I vote we at least add Double to this set, and probably the whole set of primitives. Mutable outer variables can always be added later, but they cannot be taken away later. However, if they are added, one should be able to declare them 'volatile'. Adding them will also have a rather significant impact on code meaning: By accessing a mutable from outer, that variable must switch from frame/stack-based cannot-possibly-cause-thread-issues local variable to heap-based could-be-anywhere variable that looks like a local variable. I'm not sure this is worth it. As Brian mentioned, use-cases? A compromise (which can still be added in JDK9, or later after we get more experience with lambda, but in time for JDK8) is to offer a keyword of some sort to explicitly make a mutable outer accessible from lambdas. Possibly "public" if a (context-sensitive) new keyword is not an option. Another option is to add it, but always generate a warning to make more visible the fact that a local is heap-based due to lambda access. This warning can be suppressed with a custom annotation: "@Shared int x = 10;" - Again, I vote we don't consider this for now, and revisit this topic later. It won't be difficult to add (at minimum, the compiler can sugar it into AtomicReference and wrap all reads/writes into get()/set() calls). --Reinier Zwitserloot On Sat, Nov 20, 2010 at 10:29 AM, Brian Goetz wrote: > > Hi Brian, I came to you yesterday at devoxx to ask about the rationale > > behind the decision to not capture mutable local variables. > > I admire your passion and your persistence :) > > > I understand that among several arguments, there is a strong one about > > preventing people from doing unsafe things. you gave the analogy of > > the kid, the fence and the pool. > > Safety mechanisms do not need to be perfect to be useful. Your arguments > seem to center around "I can climb the fence, so there's no point in having > it." This argument is, to be blunt, silly. The design principles of the > Java language stress safety, even if there are cases where such safety > guards can be "casted away" by a sufficiently motivated user. > > If you want to capture a not-effectively-final local variable in a closure, > then one of the following two things has to happen: > a) The lifetime of the variable must be extended to the lifetime of the > closure > b) The lifetime of the closure must be shortened to the lifetime of the > variable. > > In both cases, we would want to ensure the variable is only accessed from > the thread capturing the closure. In (a), we would be effectively creating > a whole new class of variables (in addition to the seven (or eight, > depending on how you count) already defined by the JLS); these are no longer > local variables and should not look like them. In both (a) and (b), we > would be creating a kind of restricted closure, whose execution is > restricted in space (confined to a specific thread) and/or time (confined to > the lifetime of the scope in which the variable is declared.) For > concreteness let's call these confined variables and confined lambdas. > > It is possible to create mechanisms to reify and enforce these various > types of confinement (we've discussed them extensively internally.) > However, they add nontrivial complexity to the language. > > In order to justify the complexity that these new features would generate, > there needs to be a compelling use case. When I explored this issue, I > asked a number of people to write down a use case for this. Every one wrote > some form of: > > int sum = 0; > list.forEach( #{ x -> sum += x.foo() } > > In a parellel world, this idiom is irretrievably broken. (See Guy Steele's > presentation "Organizing Functional Code for Parallel Execution, or, foldl > considered slightly harmful.") People will do this, they will do this > without thinking, and their code will be broken. It is very hard, even for > experts, to get this right. While Java has plenty of other opportunities to > create non-thread-safe code, I am not going to create an entirely new and > nearly irresistible vector for doing so. Iteration and side-effects are how > we've been trained to do things in Java, but we have to learn to do better. > > In sum, adding this feature that you (and others) want so badly seems to > add up to: > - Lots of new complexity (e.g., confined variables and lambdas) > - Nearly irresistible new areas for making errors > - All to prop up a broken programming model (iterations + side effects) > > To quote a past president: "Wouldn't be prudent." At this point in time, > it feels the risk and complexity outweighs the benefit. I would rather put > the effort into supporting map/reduce-y idioms in the libraries. The above > block is much better as: > > list.reduce( #{ x, y => x+y } ) > or > list.reduce(Reducers.SUM) > or > list.sum() > > > this thought leads to another one. in your example you are assuming > > that the foreach method will get into multithreaded stuff. > > Not quite: s/will/may/ > > > your foreach impl coud say: > > > > public void forEach(@Multithreaded Foreachable block) {...} > > We explored these issues in the JSR-166 expert group several years ago. > The basic problem is that we don't have a way of reliably expressing > "reference to thread-safe object" in the type system (either statically or > dynamically), so these things revert to being documentation rather than type > assertions. > > > for that matter, you should probably name your loop method forEachMT. > > Under consideration. Though my intuition is that (a) such a convention > will be hard to stick to and (b) in five years it will probably look silly. > > > > > From neal at gafter.com Mon Nov 22 19:53:16 2010 From: neal at gafter.com (Neal Gafter) Date: Mon, 22 Nov 2010 19:53:16 -0800 Subject: capturing (or not) mutable local variables In-Reply-To: <4CEB05E8.5000700@oracle.com> References: <4CEB05E8.5000700@oracle.com> Message-ID: On Monday, November 22, 2010, Brian Goetz wrote: > In any case, I think it is time to close this discussion. ?You've made your > point, some people agree with you, and some others disagree. ?But we do not > intend to lift this restriction at this time, and I don't think further > discussion will bring to light any issues that have not already been > considered. ?We can always revisit this issue in a future iteration (choosing > not to lift a restriction now does not foreclose on our ability to do so later). There is a way to satisfy both parties. Have the specification mandate a warning, and provide a way for the programmer to suppress the warning, effectively saying "I know what I'm doing.". (a way to escalate it to an error is good too) That's the approach taken with the latest revisions of BGGA and CfJ. From fweimer at bfk.de Tue Nov 23 06:04:27 2010 From: fweimer at bfk.de (Florian Weimer) Date: Tue, 23 Nov 2010 14:04:27 +0000 Subject: capturing (or not) mutable local variables In-Reply-To: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> (Brian Goetz's message of "Sat\, 20 Nov 2010 10\:29\:33 +0100") References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> Message-ID: <82sjysnnkk.fsf@mid.bfk.de> * Brian Goetz: > In order to justify the complexity that these new features would > generate, there needs to be a compelling use case. When I explored > this issue, I asked a number of people to write down a use case for > this. Every one wrote some form of: > > int sum = 0; > list.forEach( #{ x -> sum += x.foo() } With anonymous classes, it's used quite often for returning more values than the signature of a callback function permits. Can we at least add ObjectReference, IntReference, LongReference et al. to the JDK, and discourage abusing arrays for this purpose? > In a parellel world, this idiom is irretrievably broken. I fear that such an extreme focus on concurrent programming is rather unhealthy for a general-purpose language. -- Florian Weimer BFK edv-consulting GmbH http://www.bfk.de/ Kriegsstra?e 100 tel: +49-721-96201-1 D-76133 Karlsruhe fax: +49-721-96201-99 From brian.goetz at Oracle.com Tue Nov 23 06:39:07 2010 From: brian.goetz at Oracle.com (Brian Goetz) Date: Tue, 23 Nov 2010 09:39:07 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: <82sjysnnkk.fsf@mid.bfk.de> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <82sjysnnkk.fsf@mid.bfk.de> Message-ID: <4CEBD20B.50902@oracle.com> >> In a parellel world, this idiom is irretrievably broken. > > I fear that such an extreme focus on concurrent programming is rather > unhealthy for a general-purpose language. How can you possibly (with a straight face) describe the choice to *not* add a controversial feature as being an "unhealthily extreme focus"? Seems to me the rhetoric in this discussion has left the realm of reality. From jesse.sightler at gmail.com Tue Nov 23 06:47:55 2010 From: jesse.sightler at gmail.com (Jesse Sightler) Date: Tue, 23 Nov 2010 09:47:55 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: <4CEBD20B.50902@oracle.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <82sjysnnkk.fsf@mid.bfk.de> <4CEBD20B.50902@oracle.com> Message-ID: It's simple... there's a feeling out there that java can no longer move forward because the language cannot change due to controversy. If the modest "controversy" over this is enough to hold it back, it will only serve to feed that fire, IMO. I mean seriously, we have people in here who think that a small thing like this should be in the roadmap for JFK 9 (~2016-2018). :-) On Nov 23, 2010 9:39 AM, "Brian Goetz" wrote: >>> In a parellel world, this idiom is irretrievably broken. >> >> I fear that such an extreme focus on concurrent programming is rather >> unhealthy for a general-purpose language. > > How can you possibly (with a straight face) describe the choice to *not* add a > controversial feature as being an "unhealthily extreme focus"? Seems to me > the rhetoric in this discussion has left the realm of reality. > > > From brian.goetz at oracle.com Tue Nov 23 06:58:18 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 23 Nov 2010 09:58:18 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <82sjysnnkk.fsf@mid.bfk.de> <4CEBD20B.50902@oracle.com> Message-ID: <4CEBD68A.8030807@oracle.com> The language and platform are moving forward plenty. I realize there are those who would like to see it move forward faster, and as a Java programmer, I have some sympathy for that position. You are welcome to state your opinions here, and make your case. But at some point, by continuing to argue in this forum, you are *holding back* the platform by interfering with those that are actually moving it forward. On 11/23/2010 9:47 AM, Jesse Sightler wrote: > It's simple... there's a feeling out there that java can no longer move > forward because the language cannot change due to controversy. > > If the modest "controversy" over this is enough to hold it back, it will only > serve to feed that fire, IMO. > > I mean seriously, we have people in here who think that a small thing like > this should be in the roadmap for JFK 9 (~2016-2018). :-) > > On Nov 23, 2010 9:39 AM, "Brian Goetz" > wrote: > >>> In a parellel world, this idiom is irretrievably broken. > >> > >> I fear that such an extreme focus on concurrent programming is rather > >> unhealthy for a general-purpose language. > > > > How can you possibly (with a straight face) describe the choice to *not* add a > > controversial feature as being an "unhealthily extreme focus"? Seems to me > > the rhetoric in this discussion has left the realm of reality. > > > > > > From fweimer at bfk.de Tue Nov 23 07:25:12 2010 From: fweimer at bfk.de (Florian Weimer) Date: Tue, 23 Nov 2010 15:25:12 +0000 Subject: capturing (or not) mutable local variables In-Reply-To: <4CEBD20B.50902@oracle.com> (Brian Goetz's message of "Tue\, 23 Nov 2010 09\:39\:07 -0500") References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <82sjysnnkk.fsf@mid.bfk.de> <4CEBD20B.50902@oracle.com> Message-ID: <827hg4njtz.fsf@mid.bfk.de> * Brian Goetz: >>> In a parellel world, this idiom is irretrievably broken. >> >> I fear that such an extreme focus on concurrent programming is rather >> unhealthy for a general-purpose language. > > How can you possibly (with a straight face) describe the choice to > *not* add a controversial feature as being an "unhealthily extreme > focus"? Sorry, I just don't think "it doesn't work in a concurrent program" as a valid argument. Hardly anything works well in large, concurrenct systems, after all, so this argument is always valid in some way. I would certainly accept the argument for features which cause global concurrency issues (examples for that are POSIX APIs such as umask, chdir, setuid, fork/sigprocmask/waitpid etc.), but here, we're dealing with a potential local correctness issue which would not affect indepedent code on the same VM at all, including code which uses the same feature correctly. Furthermore, when the compiler detects non-local mutation of a variable within a closure (which could even include class members), it could mark the generated object in some way, and a hypothetical parallel forEach() could check this flag and throw. This means that you'd get a helpful exception, instead of silent corruption. (I don't think you should express this in the SAM type itself because you can use interfaces such as Runnable in both parallel and non-parallel contexts.) On the other hand, implementing *efficient* closures over mutable variables is surprisingly difficult, particularly if you want to avoid space leaks, so I certainly won't argue strongly for them. I think they would provide a useful addition to the language, though. -- Florian Weimer BFK edv-consulting GmbH http://www.bfk.de/ Kriegsstra?e 100 tel: +49-721-96201-1 D-76133 Karlsruhe fax: +49-721-96201-99 From bobfoster at gmail.com Tue Nov 23 08:41:40 2010 From: bobfoster at gmail.com (Bob Foster) Date: Tue, 23 Nov 2010 08:41:40 -0800 Subject: capturing (or not) mutable local variables Message-ID: > Seems to me the rhetoric in this discussion has left the realm of reality. When you find that an argument just can't be put to bed, it's often a sign you're on the wrong side of it. Bob Foster From pdoubleya at gmail.com Tue Nov 23 10:30:41 2010 From: pdoubleya at gmail.com (Patrick Wright) Date: Tue, 23 Nov 2010 19:30:41 +0100 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: Dear fellow lambda-dev followers Personally, I am grateful that the group at Oracle working on lambdas is maintaining an open discussion with the community. To the best of my recollection, this was not standard practice in earlier releases. As such, I think it behooves us community members to participate respectfully in this forum. There are plenty of other discussion forums where these topics may be endlessly debated. I suggest we use this one to offer constructive feedback on the proposals, and that once a decision has been made, we move on. If community members have more to say after that point, they are welcome to make their voices heard elsewhere. Let's not spoil a good thing: an open forum for technical discussion on improving the Java language where everyone is welcome to follow, to reflect, and to chime in. It is pretty clear than on this one particular topic, a decision has been made, at least for the time being. Let's move on. Best regards, Patrick From brian.goetz at oracle.com Tue Nov 23 11:01:11 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 23 Nov 2010 14:01:11 -0500 Subject: SAM resolution Message-ID: <4CEC0F77.3000601@oracle.com> If I have: interface Mapper { U map(T t); } interface IntMapper { int map(T t); } and class Stream { Stream map(Mapper mapper); IntStream map(IntMapper mapper); } and client code stream.map(#{ f -> f.someIntField }); I get this error: reference to map is ambiguous, both method map(Mapper) in Stream and method map(IntMapper) in Stream match stream.map(#{ f -> f.a }); ^ where U,T are type-variables: U extends Object declared in method map(Mapper) T extends Object declared in interface Stream Now, there are clearly two SAM types that are applicable here, so I understand the warning. But the warning seems "surprising" in a sense; in method resolution, we "prefer" the match that doesn't require boxing to the one that does. So if we have overloaded m(Integer x) and m(int x) and call m(3), we don't get an ambiguity error. It is likely to be a common case where people will define "specialized" primitive versions of methods along with generic ones. From brian.goetz at oracle.com Tue Nov 23 11:31:39 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 23 Nov 2010 14:31:39 -0500 Subject: Defender methods and compatibility Message-ID: <4CEC169B.3040300@oracle.com> Here are our latest thoughts on compatibility promises for various operations on extension methods (adding a default to an existing method, adding a new method with a default, removing a default from a method, changing a default, etc.) Our first instinct was to make an analogy with operations on classes. Consider: de-abs: Taking an existing abstract method and adding a body re-abs: Converting a concrete method into an abstract one add-meth: Adding a new concrete method rem-meth: Removing a concrete method mod-meth: Modify the body of a concrete method Recall that a binary-compatible (BC) change does not prevent linkage with pre-existing binaries, while a source-compatible (SC) changes does not prevent compilation with pre-existing sources. For example, adding a method to an interface is BC (because no pre-existing binary calls the new method), but it is not SC (because a pre-existing class declaration that implements the interface will now fail to compile). With BC and SC in mind, the existing rules are: de-abs: BC, SC re-abs: not BC, not SC add-meth: BC, SC* rem-meth: not BC, not SC mod-meth: BC, SC *This would fail to be SC only if it created an invalid overloading, e.g., adding a method whose signature is incompatible with a method in a subclass, such as adding a method "int foo()" when a subclass has a method "float foo()". If we consider the operations on interfaces: add-def: Add a default to an existing non-extension method rem-def: Remove a default from an extension method add-extn: Add a new method with a default rem-extn: Remove a method with a default mod-extn: Modify the default on an extension method Ideally, these should be analogous to the existing operations: add-def is-like de-abs (and therefore hopefully BC/SC) rem-def is-like re-abs (no compatibility promise) add-extn is-like add-meth (and therefore hopefully BC/SC) rem-extn is-like rem-meth (no compatibility promise) mod-extn is-like mod-meth (and therefore hopefully BC/SC) These analogies only hold true to a point -- when the modifications create no invalid multiple inheritance (i.e., two unrelated interfaces provide the same name and signature but different defaults.) This is the multiple-inheritance analogue of the asterisk next to SC for add-meth (creating an invalid overloading). Whereas this is only an issue with add-meth in the existing cases, it also becomes an issue for add-def and mod-extn with extension methods. Here's an example of a change that is not source-compatible: interface A { extension void m() default X.a } interface B { } class C implements A, B { } // Now change and recompile B only as interface B { extension void m() default X.b } The change to B is not source compatible because C can no longer be recompiled. Also, it is not binary compatible because a pre-existing binary that calls C.m() is now ambiguous. So while we hoped that add-extn would be BC and SC, it is neither. (The same goes for add-def and mod-extn, since the example can be expressed in terms of adding/modifying defaults.) However, we can arrange things so that the change to B _becomes_ binary compatible. Observe that if a program can _ever_ be compiled, then it can _always_ be made to link. If we record the initial "known good" configuration of A+B+C in C's class file, then binaries that link against A+B+C can be made to see no ambiguity even after the new B is introduced, rendering the change to B binary compatible. Only an attempt to recompile C will give an error, due to the fundamental source incompatibility introduced by the new B. The mechanism to record and use a "known good" configuration will be explained separately. The key point is that the familiar guarantee of binary compatibility continues to be available, even with strange new multiple inheritances due to separate compilation of extension methods. So we have the desired behavior: add-def - BC, SC** rem-def - no compatibility promise add-extn - BC, SC** rem-extn - no compatibility promise mod-extn - BC, SC** Where the SC** means "providing that the modification does not create the situation where a class multiply inherits methods with the same signature but different defaults." While this restriction may be unsatisfying, this is really not all that different from the existing SC* behavior of add-meth. From neal at gafter.com Tue Nov 23 15:59:12 2010 From: neal at gafter.com (Neal Gafter) Date: Tue, 23 Nov 2010 15:59:12 -0800 Subject: SAM resolution In-Reply-To: <4CEC0F77.3000601@oracle.com> References: <4CEC0F77.3000601@oracle.com> Message-ID: Overload resolution takes place in three phases: the first does not consider methods that require boxing to match, and if a unique result is found then that is the result of overload resolution. The second phase allows boxing and unboxing. It appears that the lambda conversion implemented today does not distinguish between these two phases. On Tue, Nov 23, 2010 at 11:01 AM, Brian Goetz wrote: > If I have: > > interface Mapper { U map(T t); } > interface IntMapper { int map(T t); } > > and > > class Stream { > Stream map(Mapper mapper); > IntStream map(IntMapper mapper); > } > > and client code > > stream.map(#{ f -> f.someIntField }); > > I get this error: > > reference to map is ambiguous, both method map(Mapper) in Stream > and > method map(IntMapper) in Stream match > stream.map(#{ f -> f.a }); > ^ > where U,T are type-variables: > U extends Object declared in method map(Mapper) > T extends Object declared in interface Stream > > Now, there are clearly two SAM types that are applicable here, so I > understand > the warning. But the warning seems "surprising" in a sense; in method > resolution, we "prefer" the match that doesn't require boxing to the one > that > does. So if we have overloaded > m(Integer x) > and > m(int x) > > and call m(3), we don't get an ambiguity error. > > It is likely to be a common case where people will define "specialized" > primitive versions of methods along with generic ones. > > > > > > From neal at gafter.com Tue Nov 23 16:11:34 2010 From: neal at gafter.com (Neal Gafter) Date: Tue, 23 Nov 2010 16:11:34 -0800 Subject: Defender methods and compatibility In-Reply-To: <4CEC169B.3040300@oracle.com> References: <4CEC169B.3040300@oracle.com> Message-ID: On Tue, Nov 23, 2010 at 11:31 AM, Brian Goetz wrote: > While this restriction may be unsatisfying, this is really not all that > different from the existing SC* behavior of add-meth. > Indeed this (binary incompatibility with mod-extn) is particularly unsatisfying because the binary compatibility rules are designed specifically to be insensitive to changes of implementation details. The code which implements an extension method is just such an implementation detail. The analogy to add-meth is inappropriate because add-meth is a change to the public API (not merely implementation details). Fortunately, you can do much better by removing the special rule about allowing inheriting multiple extension methods with the same default (without ambiguity). Without this rule, programmers will explicitly resolve the ambiguity at the point where multiple extensions with the same signature are inherited. In that case changing an extension method's default is both source and binary compatible. Given that a satisfying solution exists, there is no reason to settle for an unsatisfying one. From alex.buckley at oracle.com Tue Nov 23 16:33:18 2010 From: alex.buckley at oracle.com (Alex Buckley) Date: Tue, 23 Nov 2010 16:33:18 -0800 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> Message-ID: <4CEC5D4E.4060903@oracle.com> On 11/23/2010 4:11 PM, Neal Gafter wrote: > On Tue, Nov 23, 2010 at 11:31 AM, Brian Goetz wrote: > >> While this restriction may be unsatisfying, this is really not all that >> different from the existing SC* behavior of add-meth. > > Indeed this (binary incompatibility with mod-extn) is particularly > unsatisfying because the binary compatibility rules are designed > specifically to be insensitive to changes of implementation details. You're misreading the asterisks. mod-extn is binary compatible always. > Fortunately, you can do much better by removing the special rule about > allowing inheriting multiple extension methods with the same default > (without ambiguity). Without this rule, programmers will explicitly resolve > the ambiguity at the point where multiple extensions with the same signature > are inherited. In that case changing an extension method's default is both > source and binary compatible. It's impossible to guarantee source compatibility when changing an extension method's default, unless you force a class whose superinterfaces have _any_ extension methods to choose a preferred extension method even when there's no ambiguity. But the whole point is to avoid changing implementation classes except where absolutely necessary. In other words: start with a class whose superinterfaces have no ambiguity about multiple extension methods; now recompile one of those superinterfaces so there's an ambiguity; the class cannot now be recompiled without change. We already support that change (see section 6 of the V3 defender methods doc) and moreover it's only necessary at compile-time when a real ambiguity has been introduced. Alex From neal at gafter.com Tue Nov 23 16:55:30 2010 From: neal at gafter.com (Neal Gafter) Date: Tue, 23 Nov 2010 16:55:30 -0800 Subject: Defender methods and compatibility In-Reply-To: <4CEC5D4E.4060903@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> Message-ID: On Tue, Nov 23, 2010 at 4:33 PM, Alex Buckley wrote: > On 11/23/2010 4:11 PM, Neal Gafter wrote: > >> On Tue, Nov 23, 2010 at 11:31 AM, Brian Goetz > >wrote: >> >> While this restriction may be unsatisfying, this is really not all that >>> different from the existing SC* behavior of add-meth. >>> >> >> Indeed this (binary incompatibility with mod-extn) is particularly >> unsatisfying because the binary compatibility rules are designed >> specifically to be insensitive to changes of implementation details. >> > > You're misreading the asterisks. mod-extn is binary compatible always. Not with the currently published specification. In any case, my comment holds true for source compatibility as well: the language is currently designed so that the change of an implementation detail in one place does not break source compatibility elsewhere. The current proposal undermines that design goal. Fortunately, you can do much better by removing the special rule about >> allowing inheriting multiple extension methods with the same default >> (without ambiguity). Without this rule, programmers will explicitly >> resolve >> the ambiguity at the point where multiple extensions with the same >> signature >> are inherited. In that case changing an extension method's default is >> both >> source and binary compatible. >> > > It's impossible to guarantee source compatibility when changing an > extension method's default, unless you force a class whose superinterfaces > have _any_ extension methods to choose a preferred extension method even > when there's no ambiguity. I hear what you're saying but I don't understand why you believe it. Perhaps your concept of "even when there's no ambiguity" includes reference to details of the implementation of extension methods (i.e. what code is executed), which is exactly the kind of error I'm proposing you fix. If you mean something else, perhaps you can convince me by demonstrating it. > But the whole point is to avoid changing implementation classes except > where absolutely necessary. > If the specification for extension methods did not make its ambiguity rules refer to the implementation of the extension methods (and I suggest that it should not), then there is no way that changing the default could introduce a source incompatibility. In other words: start with a class whose superinterfaces have no ambiguity > about multiple extension methods; now recompile one of those superinterfaces > so there's an ambiguity; the class cannot now be recompiled without change. Indeed, it should not be possible to "recompile" to introduce an ambiguity. Only changes to the public API should be capable of affecting binary or source compatibility. From isidore at setgame.com Tue Nov 23 17:13:58 2010 From: isidore at setgame.com (Llewellyn Falco) Date: Tue, 23 Nov 2010 17:13:58 -0800 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: proposal for local variable capture: There are many cases in which local variables are useful in the scope of lambdas, but the basically break down into 2 cases 1: need to read a local variable 2: need to change a local variable The purpose of the this proposal is to suggest a way that is but clear and easy to write, but also follows "the pit of success"; namely, the code does not act in a Surprising manner to the average, uninformed developer. Let's look at the first case: read a local variable. // create filters for the numbers 1 - 10 for(int i = 0; i < 10; i++) { filter(#{f -> f == i}); } Of course this doesn't work in many languages, and won't compile in java. The work around is of course to // create filters for the numbers 1 - 10 for(int i = 0; i < 10; i++) { final int i2 = i; filter(#{f -> f == i2}); } I would like to suggest that java lambda's do this. Namely, if a local variable is *only read* create a second reference to the local variable, and transparently use it instead. Doing this would create the "pit of success" that main other languages have missed. Namely, the code would do as expected, even to the uninformed java programmer. The second case is where you need to track some common variable. int i = 0; createUniqueIds(#{i++}); Here, it's important to be able to have access to i otherwise it would conflict. this also will not compile in java. the workaround of course is int i = 0; final int[] i2 = {i}; createUniqueIds(#{i2[0]++}); This of course becomes an issue inside of a loop int j = 0; for(int i = 0; i < 10; i++) { createUniqueIds(#{ j++}); } does this create 10 different j's. NO, again the "pit of success" would imply no duplicate Id's across all 10 calls to createUniqueIds so the result should be int j = 0; final int j2[] = {j}; for(int i = 0; i < 10; i++) { createUniqueIds(#{ j2[0]++}); } So I am suggesting that the way local variables are handled differs based on whether or not the use of the variable is Read only access or Write access. To be clear, assume the following int j = 0; for(int i = i; i < 10; i++) { createUniqueIds(#{ j+= i}); } would pre-compile to int j = 0; final int j2[] = {j}; for(int i = i; i < 10; i++) { final int i2 = i; createUniqueIds(#{ j2[0]+= i2}); } I believe handling these 2 cases separately would be the least surprising to everyone, avoid many common mistakes made in other languages, and yet keep the syntax clean and simple. Achieving the "pit of success" If there is a situation I haven't thought of, that would create a puzzling pit of failure, please let me know (I'm not sure I've gotten everything...) Thanks, Llewellyn Falco http://bit.ly/lambdas From alex.buckley at oracle.com Tue Nov 23 17:13:41 2010 From: alex.buckley at oracle.com (Alex Buckley) Date: Tue, 23 Nov 2010 17:13:41 -0800 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> Message-ID: <4CEC66C5.3040505@oracle.com> On 11/23/2010 4:55 PM, Neal Gafter wrote: > On Tue, Nov 23, 2010 at 4:33 PM, Alex Buckley You're misreading the asterisks. mod-extn is binary compatible always. > > Not with the currently published specification. Right; Brian's mail stated there are details yet to be published. > In any case, my comment holds true for source compatibility as well: > the language is currently designed so that the change of an > implementation detail in one place does not break source > compatibility elsewhere. The current proposal undermines that design > goal. As does any proposal to have method bodies directly in interfaces. Let's cut to the heart of the issue: > In other words: start with a class whose superinterfaces have no > ambiguity about multiple extension methods; now recompile one of > those superinterfaces so there's an ambiguity; the class cannot now > be recompiled without change. > > Indeed, it should not be possible to "recompile" to introduce an > ambiguity. Only changes to the public API should be capable of > affecting binary or source compatibility. I agree with the first sentence; an ambiguity results in a compile-time error. As for the second sentence, the sense of an extension method is precisely that its default (whether given by name or by value in the interface) becomes part of the method's logical "signature". How could it be otherwise, given declaration-site extensions and separate compilation? Alex From collin.fagan at gmail.com Tue Nov 23 18:22:20 2010 From: collin.fagan at gmail.com (Collin Fagan) Date: Tue, 23 Nov 2010 20:22:20 -0600 Subject: capturing (or not) mutable local variables In-Reply-To: References: Message-ID: Okay, that's it, look it's over people, you lose, no mutable variable capture for you. It's been stated by the actual people *doing the work* that this is *not in scope*. Dead. Done. Over. As software engineers we all have to manage scope in our projects and sometimes that entails telling people they don't get what they want. This is one of those times. You can be right and still be wrong, this is one of those situations. I'm sorry, Collin On Tue, Nov 23, 2010 at 7:13 PM, Llewellyn Falco wrote: > proposal for local variable capture: > > There are many cases in which local variables are useful in the scope of > lambdas, but the basically break down into 2 cases > > 1: need to read a local variable > 2: need to change a local variable > > The purpose of the this proposal is to suggest a way that is but clear and > easy to write, but also follows "the pit of success"; namely, the code does > not act in a Surprising manner to the average, uninformed developer. > > Let's look at the first case: read a local variable. > > // create filters for the numbers 1 - 10 > for(int i = 0; i < 10; i++) > { > filter(#{f -> f == i}); > } > > Of course this doesn't work in many languages, and won't compile in java. > > The work around is of course to > // create filters for the numbers 1 - 10 > for(int i = 0; i < 10; i++) > { > final int i2 = i; > filter(#{f -> f == i2}); > } > > I would like to suggest that java lambda's do this. Namely, if a local > variable is *only read* create a second reference to the local variable, > and > transparently use it instead. > > Doing this would create the "pit of success" that main other languages have > missed. Namely, the code would do as expected, even to the uninformed java > programmer. > > > The second case is where you need to track some common variable. > > int i = 0; > createUniqueIds(#{i++}); > > Here, it's important to be able to have access to i otherwise it would > conflict. > this also will not compile in java. > > the workaround of course is > int i = 0; > final int[] i2 = {i}; > createUniqueIds(#{i2[0]++}); > > > This of course becomes an issue inside of a loop > int j = 0; > for(int i = 0; i < 10; i++) > { > createUniqueIds(#{ j++}); > } > does this create 10 different j's. NO, again the "pit of success" would > imply no duplicate Id's across all 10 calls to createUniqueIds > > so the result should be > int j = 0; > final int j2[] = {j}; > for(int i = 0; i < 10; i++) > { > createUniqueIds(#{ j2[0]++}); > } > > So I am suggesting that the way local variables are handled differs based > on > whether or not the use of the variable is Read only access or Write access. > > To be clear, assume the following > int j = 0; > for(int i = i; i < 10; i++) > { > createUniqueIds(#{ j+= i}); > } > > would pre-compile to > > int j = 0; > final int j2[] = {j}; > for(int i = i; i < 10; i++) > { > final int i2 = i; > createUniqueIds(#{ j2[0]+= i2}); > } > > > I believe handling these 2 cases separately would be the least surprising > to > everyone, avoid many common mistakes made in other languages, and yet keep > the syntax clean and simple. Achieving the "pit of success" > > If there is a situation I haven't thought of, that would create a puzzling > pit of failure, please let me know (I'm not sure I've gotten everything...) > > > > Thanks, > > Llewellyn Falco > http://bit.ly/lambdas > > From neal at gafter.com Tue Nov 23 19:44:35 2010 From: neal at gafter.com (Neal Gafter) Date: Tue, 23 Nov 2010 19:44:35 -0800 Subject: Defender methods and compatibility In-Reply-To: <4CEC66C5.3040505@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> Message-ID: On Tue, Nov 23, 2010 at 5:13 PM, Alex Buckley wrote: > On 11/23/2010 4:55 PM, Neal Gafter wrote:In any case, my comment holds true > for source compatibility as well: > >> the language is currently designed so that the change of an >> implementation detail in one place does not break source >> compatibility elsewhere. The current proposal undermines that design >> goal. >> > > As does any proposal to have method bodies directly in interfaces. > I don't believe that is true. > Let's cut to the heart of the issue: > > > In other words: start with a class whose superinterfaces have no >> ambiguity about multiple extension methods; now recompile one of >> those superinterfaces so there's an ambiguity; the class cannot now >> be recompiled without change. >> >> Indeed, it should not be possible to "recompile" to introduce an >> ambiguity. Only changes to the public API should be capable of affecting >> binary or source compatibility. >> > > I agree with the first sentence; an ambiguity results in a compile-time > error. As for the second sentence, the sense of an extension method is > precisely that its default (whether given by name or by value in the > interface) becomes part of the method's logical "signature". How could it be > otherwise, given declaration-site extensions and separate compilation? It could easily be otherwise: place the extension method bodies in the interfaces and treat every method body as distinct from every other method body. From neal at gafter.com Tue Nov 23 20:13:31 2010 From: neal at gafter.com (Neal Gafter) Date: Tue, 23 Nov 2010 20:13:31 -0800 Subject: capturing (or not) mutable local variables In-Reply-To: <4CEBD20B.50902@oracle.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <82sjysnnkk.fsf@mid.bfk.de> <4CEBD20B.50902@oracle.com> Message-ID: On Tue, Nov 23, 2010 at 6:39 AM, Brian Goetz wrote: > >> In a parellel world, this idiom is irretrievably broken. > > > > I fear that such an extreme focus on concurrent programming is rather > > unhealthy for a general-purpose language. > > How can you possibly (with a straight face) describe the choice to *not* > add a > controversial feature as being an "unhealthily extreme focus"? Seems to me > the rhetoric in this discussion has left the realm of reality. > "capture of mutable variables" is not a feature. Capture of variables (i.e. lexical scoping) is a feature. Mutable variables is a feature. The intersection of the two is just a confluence of two orthogonal features. Independently designing the intersection of two otherwise orthogonal language features on the basis of use cases is a classic sign of amateur language design. Failure of imagination - that is, failure to make language constructs orthogonal because you aren't quite sure how people will need to use them together - is one of the most common and severe errors you can make in programming language design. Cheers, Neal From alex.buckley at oracle.com Wed Nov 24 12:00:51 2010 From: alex.buckley at oracle.com (Alex Buckley) Date: Wed, 24 Nov 2010 12:00:51 -0800 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> Message-ID: <4CED6EF3.5070408@oracle.com> On 11/23/2010 7:44 PM, Neal Gafter wrote: > I agree with the first sentence; an ambiguity results in a > compile-time error. As for the second sentence, the sense of an > extension method is precisely that its default (whether given by > name or by value in the interface) becomes part of the method's > logical "signature". How could it be otherwise, given > declaration-site extensions and separate compilation? > > It could easily be otherwise: place the extension method bodies in the > interfaces and treat every method body as distinct from every other > method body. I don't understand how this preserves source compatibility. Start with the following types that compile and run: interface I {} interface J {} class C implements I,J {} Now modify and recompile I only: interface I { extension void m() { BODY_I } } It turns out to be source-compatible because C would still compile. Now modify and recompile J only: interface J { extension void m() { BODY_J } } Now C would not compile. We all agree C must be changed so it declares m and (probably) delegates to I.m or J.m. But the add-extn to J remains a source-incompatible change. Alex P.S. add-extn and mod-extn on an interface are SC if _all_ classes that implement the interface define (by declaration or by class inheritance) the method that gained/changed extension. But there's no way to check it, of course. From neal at gafter.com Wed Nov 24 14:38:25 2010 From: neal at gafter.com (Neal Gafter) Date: Wed, 24 Nov 2010 14:38:25 -0800 Subject: Defender methods and compatibility In-Reply-To: <4CED6EF3.5070408@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> Message-ID: On Wed, Nov 24, 2010 at 12:00 PM, Alex Buckley wrote: > Now C would not compile. We all agree C must be changed so it declares m > and (probably) delegates to I.m or J.m. But the add-extn to J remains a > source-incompatible change. > Right. We agree that add-extn is a source-incompatible change. My point was that mod-extn should be a source-compatible change, but you've specified it to be a source-incompatible change. Cheers, Neal From alex.buckley at oracle.com Wed Nov 24 15:29:57 2010 From: alex.buckley at oracle.com (Alex Buckley) Date: Wed, 24 Nov 2010 15:29:57 -0800 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> Message-ID: <4CED9FF5.2080308@oracle.com> On 11/24/2010 2:38 PM, Neal Gafter wrote: > Now C would not compile. We all agree C must be changed so it > declares m and (probably) delegates to I.m or J.m. But the add-extn > to J remains a source-incompatible change. > > Right. We agree that add-extn is a source-incompatible change. My > point was that mod-extn should be a source-compatible change, but you've > specified it to be a source-incompatible change. There has been some confusion - you started by quoting Brian's comment on add-meth, which should be compared to add-extn, but you raised a point about mod-extn and its relationship to mod-meth. The initial summary mail should have compared add-extn specifically to add-meth, and mod-extn specifically to mod-meth. When I wrote earlier about "changing an extension method's default", I was mainly thinking of adding a default, which we all agree is not guaranteed SC. Specifically, I was thinking of changing an interface method into an extension method by adding a default. That's one case of add-extn. (The other is adding an extension method signature from scratch.) Anyway, the good news is that mod-extn _is_ SC. That's true whether a default is specified in the interface via a method body or a method name. If specified via a method name, then independent mod-extn actions (or add-extn - there it is again!) on different interfaces _may_ still be SC for an implementation class. Some people see this as an optimization we can leave out; that may be so, but it is not a prima facie reason for preferring method bodies. Alex From alex.buckley at oracle.com Wed Nov 24 15:48:07 2010 From: alex.buckley at oracle.com (Alex Buckley) Date: Wed, 24 Nov 2010 15:48:07 -0800 Subject: Defender methods and compatibility In-Reply-To: <4CED9FF5.2080308@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> Message-ID: <4CEDA437.1070904@oracle.com> On 11/24/2010 3:29 PM, Alex Buckley wrote: > Anyway, the good news is that mod-extn _is_ SC. That's true whether a > default is specified in the interface via a method body or a method name. Should have said: mod-extn _may be_ SC. When an implementation class doesn't override the extension, it's possible for previously-identical extension methods to diverge (i.e. one changes its default) and break SC. Alex From neal at gafter.com Wed Nov 24 17:22:41 2010 From: neal at gafter.com (Neal Gafter) Date: Wed, 24 Nov 2010 17:22:41 -0800 Subject: Defender methods and compatibility In-Reply-To: <4CEDA437.1070904@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> Message-ID: On Wed, Nov 24, 2010 at 3:48 PM, Alex Buckley wrote: > On 11/24/2010 3:29 PM, Alex Buckley wrote: > >> Anyway, the good news is that mod-extn _is_ SC. That's true whether a >> default is specified in the interface via a method body or a method name. >> > > Should have said: mod-extn _may be_ SC. When an implementation class > doesn't override the extension, it's possible for previously-identical > extension methods to diverge (i.e. one changes its default) and break SC. > Understood. That is the particular part of the specification that I take issue with. mod-extn (changing the code of a default method) should (in my opinion) always be source compatible, as the code of a method is a prime example of an implementation detail. From dl at cs.oswego.edu Fri Nov 26 02:58:23 2010 From: dl at cs.oswego.edu (Doug Lea) Date: Fri, 26 Nov 2010 05:58:23 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: <4CE97E88.5010803@bothner.com> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> <4CE97E88.5010803@bothner.com> Message-ID: <4CEF92CF.8090600@cs.oswego.edu> Finally reading this discussion... On 11/21/10 15:18, Per Bothner wrote: > On 11/20/2010 12:44 PM, Brian Goetz wrote: >>> I think I'm hearing that mutating a local variable from a closure is just a >>> bad idea in general right? >> >> For a closure that might conceivably be executed in another thread, this is correct. > > Agreed - but how is that different from mutating a field or array element > from a closure that might conceivably be executed in another thread? > You're just forcing the user to do by hand to work around a restriction. I think the main goal here is to preserve the "shallow data race freedom of locals" property of Java: Writes to any variable syntactically declared as a local (or argument) can never be subject to a data race. In conjunction with the other main property of Java locals that they are definitely assigned, this can be used to provide a simple partial correctness guarantee about the integrity of reads. Which can then be used as a base step for more thorough reasoning about safety. The shallowness arises in case a local is a reference to an object and is dereferenced (accessed), in which case programmers have more work to do proving race-freedom of the accesses. The limitation to shallow safety seems to be pragmatic decision: Deeper guarantees would require non-modular checks (as in, trying to verify that data-race freedom held across calls to possible virtual methods) as well as concurrency awareness by front-end compilers, plus some means of programmers annotating the shapes of data structures and partitioning, as in those in DPJ (http://dpj.cs.uiuc.edu/DPJ/Home.html) that can help check that all in-place updates of elements of arrays or collections are fully partitioned across threads. (Aside: Many efficient multicore algorithms entail in-place updates -- the main advantages of their design (versus other forms of multiprocessors) stem from their typically faster access to shared memory.) The workarounds like using 1-slot arrays add a level of indirection to subvert the shallow-only provision. Which seems tolerable. It would be even better to come up with capture rules that guaranteed this property while still also allowing more flexible usages. The suggestion to extend "could be final" to something akin to having a unique SSA form is one step. Going even a bit further than this would be hard because front-end compilers are not thread-aware. So among other things they cannot detect the canonically bad idea of using accumulator variables in parallel loops. However, perhaps the folks arguing for more flexible usage could discover more powerful yet practical techniques that would still preserve the shallow data-race freedom property in more cases, and thus allow some of their target usages. -Doug From dl at cs.oswego.edu Fri Nov 26 04:45:57 2010 From: dl at cs.oswego.edu (Doug Lea) Date: Fri, 26 Nov 2010 07:45:57 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: <4CEF92CF.8090600@cs.oswego.edu> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> <4CE97E88.5010803@bothner.com> <4CEF92CF.8090600@cs.oswego.edu> Message-ID: <4CEFAC05.9050402@cs.oswego.edu> On 11/26/10 05:58, Doug Lea wrote: > However, perhaps the folks arguing for more flexible > usage could discover more powerful yet practical techniques > that would still preserve the shallow data-race freedom > property in more cases, and thus allow some of their target usages. > One way is to simply preserve this property, period: Suppose the translation mechanics for mutable variables were in terms of ThreadLocal boxed objects, rather than those possibly shared across threads. In other words class C { void f() { int i = 0; ... #{ ... ++i; ... } } } becomes translated to something like: class C { void f() { ThreadLocal ti = ...; ... #{ ... ti.increment(); ... } } } It would be challenging to come up good rules to maximize chances that this is done only when people really meant to do it. The various "mistake" cases (like for-loop indices) seem to outnumber the intentional ones. And further to teach front-end compilers, AOT bytecode optimizers, and/or JITs to understand these constructions well enough to avoid all the overhead when it is statically determinable that lambda can be inlined/macro-expanded in a way that allows variables to just remain as locals. It would also provide some fodder for pushing ThreadLocal support deeper into the language/compilers/runtimes to help improve performance. And the net impact on constructions like accumulators in parallel loops would be that you'd always get a safe but wrong answer. All in all, while this may be the best possible compromise (providing both mutability and guaranteed locality), I don't see a good reason to do it. However, it does suggest that if there were better syntax support for safe workarounds, people might be happier to use them. Maybe @ThreadLocal could trigger this transformation? class C { void f() { @ThreadLocal int i = 0; ... #{ ... ++i; ... } } } -Doug From markmahieu at gmail.com Fri Nov 26 09:13:44 2010 From: markmahieu at gmail.com (Mark Mahieu) Date: Fri, 26 Nov 2010 17:13:44 +0000 Subject: capturing (or not) mutable local variables In-Reply-To: <4CEF92CF.8090600@cs.oswego.edu> References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> <4CE97E88.5010803@bothner.com> <4CEF92CF.8090600@cs.oswego.edu> Message-ID: On 26 Nov 2010, at 10:58, Doug Lea wrote: > > The workarounds like using 1-slot arrays add a level of > indirection to subvert the shallow-only provision. > Which seems tolerable. It's tolerable if the workaround is employed with a good understanding of its limitations, but it has become a common approach to simple inconveniences with final variables used in relatively innocuous contexts. My fear is that it's a workaround which will find itself used in far less tolerant situations through the simple virtues of being well known and long established. So the following kind of approach... > However, it does suggest > that if there were better syntax support for safe > workarounds, people might be happier to use them. > Maybe @ThreadLocal could trigger this transformation? > > class C { > void f() { > @ThreadLocal int i = 0; > ... #{ ... ++i; ... } > } > } > > -Doug ... is one I see as a (conceptually) neat alternative to the two extremes this discussion has polarised around so far. Lambda does present a very rare opportunity to offer a different approach and have it noticed by a wide audience. Cheers, Mark From jim at pentastich.org Fri Nov 26 18:39:37 2010 From: jim at pentastich.org (Jim Mayer) Date: Fri, 26 Nov 2010 21:39:37 -0500 Subject: pure functions and the parallel evaluation of reduce-like operations Message-ID: Hi all, In the recent debate on capturing mutable local variables the "reduce" function was used as an example application of functional programming, sometimes with an implication that the form could be evaluated in parallel. It seems to me, though, that a parallel evaluation scheme is only applicable if the operator is associative (or, better, commutative and associative). There's no question that it is easier and more efficient to do parallel operations with "pure" functions, but a functional style is not sufficient to enable parallelism. In fact, in Java, even addition and multiplication of most numeric types are not associative because of overflow and loss of precision. For "well behaved" data sets things usually work well enough, but it really matters in some calculations. My question is, as we consider use cases for lambda expressions and whether to put effort into (as Brian Goetz said) "supporting map/reduce-y idioms in the libraries", do we need to consider how functions (like 'reduce') declare the constraints? How we would use functional programming idioms to enable parallelism in Java? Do we define annotations to declare intent (e.g, @Associative)? Do we have multiple reduce operations (e.g., leftReduce, rightReduce, associativeReduce, unorderedReduce)? Do we pass in the function's constraints as a parameter (e.g., List.reduce(Operation.ASSOCIATIVE, #{s v -> s + v}))? Are the mechanisms available sufficient to enable wide-spread, safe, use of the map/reduce-y idioms? --- Jim From scolebourne at joda.org Sat Nov 27 03:54:08 2010 From: scolebourne at joda.org (Stephen Colebourne) Date: Sat, 27 Nov 2010 11:54:08 +0000 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> <4CE97E88.5010803@bothner.com> <4CEF92CF.8090600@cs.oswego.edu> Message-ID: On 26 November 2010 17:13, Mark Mahieu wrote: > On 26 Nov 2010, at 10:58, Doug Lea wrote: >> The workarounds like using 1-slot arrays add a level of >> indirection to subvert the shallow-only provision. >> Which seems tolerable. > > It's tolerable if the workaround is employed with a good understanding of its limitations, but it has become a common approach to simple inconveniences with final variables used in relatively innocuous contexts. My fear is that it's a workaround which will find itself used in far less tolerant situations through the simple virtues of being well known and long established. Agreed. And there is a connection to generics wildcards too. In both cases, restrictions were/are added to the language for good reasons - to keep the language safe. (For generics I'm thinking of casting a List to a List, and many other cases as in the huge FAQ). What happens is: - the developer tries to perform the "bad" thing - the compiler stops them - the developer curses the compiler - the developer finds the quickest hack In my experience, most don't worry about WHY the thing was "bad" they just learn the hacks (and aren't worried if the hack is safe). For generics, that might be using a raw type. For no access to mutable variables, its the length one array. While I can entirely see why the mutable variable restriction is desirable from a correctness POV, I'm far from convinced that it is beneficial from a practical POV, and may actually be harmful (because only a fraction of developers understand the restriction). As such, allowing mutable access may well be better overall. Although, some kind of middle ground like Doug suggests would be good. (Note that it could be argued based on the above, that mutable access via arrays should be blocked to force an object based workaround like AtomicRefs) Stephen From thomas.andreas.jung at googlemail.com Sat Nov 27 07:45:50 2010 From: thomas.andreas.jung at googlemail.com (Thomas Jung) Date: Sat, 27 Nov 2010 16:45:50 +0100 Subject: Artifact URL changes due to move to Kenai Message-ID: Hi, the URLs of downloaded source artifacts seems to changed due to the move to Kenai. Building with "make all" works with the URLs below. Thomas diff -r 3f61197b38c8 jaxws.properties --- a/jaxws.properties Tue Oct 19 11:07:54 2010 +0100 +++ b/jaxws.properties Sat Nov 27 16:38:07 2010 +0100 @@ -28,12 +28,12 @@ jaxws_src.bundle.name= jdk7-jaxws2_2-2010_08_19.zip jaxws_src.bundle.md5.checksum=8775ccefd3b4fa2dde5155ec4b7e4ceb jaxws_src.master.bundle.dir=${drops.master.copy.base} -jaxws_src.master.bundle.url.base=https://jax-ws.dev.java.net/files/documents/4202/152532 +jaxws_src.master.bundle.url.base=http://java.net/downloads/jax-ws/JDK7/ jaf_src.bundle.name=jdk7-jaf-2010_08_19.zip jaf_src.bundle.md5.checksum=18d15dfd71117daadb332af003d08212 jaf_src.master.bundle.dir=${drops.master.copy.base} -jaf_src.master.bundle.url.base=https://jax-ws.dev.java.net/files/documents/4202/152336 +jaf_src.master.bundle.url.base=http://java.net/downloads/jax-ws/JDK7/ #jaxws_tests.bundle.name=jdk7-jaxws-tests-2009_08_28.zip #jaxws_tests.master.bundle.dir=${drops.master.copy.base} diff -r bfeed086a9c7 jaxp.properties --- a/jaxp.properties Tue Oct 19 11:07:46 2010 +0100 +++ b/jaxp.properties Sat Nov 27 16:39:41 2010 +0100 @@ -28,7 +28,7 @@ jaxp_src.bundle.name=jaxp-1_4_4.zip jaxp_src.bundle.md5.checksum=2c40a758392c4abf2d59f355240df46a jaxp_src.master.bundle.dir=${drops.master.copy.base} -jaxp_src.master.bundle.url.base=https://jaxp.dev.java.net/files/documents/913/152561 +jaxp_src.master.bundle.url.base=http://java.net/downloads/jaxp/jdk7 #jaxp_tests.bundle.name=jaxp-unittests-1_4_4.zip #jaxp_tests.bundle.md5.checksum=51845e38b02920cf5374d0331ab3a4ee From reinier at zwitserloot.com Sat Nov 27 08:44:24 2010 From: reinier at zwitserloot.com (Reinier Zwitserloot) Date: Sat, 27 Nov 2010 17:44:24 +0100 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> <4CE97E88.5010803@bothner.com> <4CEF92CF.8090600@cs.oswego.edu> Message-ID: That sounds a bit defeatist! Just because a sizable chunk of programmers casually write code that includes an active bug or is extremely fragile doesn't mean we should just throw in the towel. Work should perhaps be done on the warning / error messages. For example, include a URL to a page that serves as a tutorial for the more complex java rules, such as why you can't assign a List to a List. But, that's not a job for lambda-dev, and doesn't have to be done right now. The exact wording of error messages aren't part of the java spec, fortunately. I think PHP shows what happens when one always takes the most direct pragmatic solution to any problem. I assume I don't have to elaborate much on why PHP indicates this is not a particularly wise move. There's also a decent chance someone with absolutely no understanding as to why mutables in (non-thread-local) closures are a bad idea will nevertheless be coached into doing the right thing. The error message will doubtlessly reference the notion that _final_ locals _can_ be accessed in the closure. Making a copy final is a lot simpler than the array/AtomicX workaround, and so happens to be the superior solution too. A second benefit is that, while the one that wrote the code may well be clueless about what multicore implies, someone else reviewing this code will have an easier time identifying what's going on. This is quite java-esque - a field ref can't possibly invoke code. A local variable can't possibly be involved in a race condition. It's the basis upon which more complex reasoning can be performed. An 1-size array/AtomicX is an easily visible red flag. At some point I'd like to see a still visible but less convoluted way of doing this, for example by adding a keyword that can be applied to a local, but there's no particular need to do this for the first release of closures in java. An advantage of doing it later is that there will be far more experience to base further language changes on. --Reinier Zwitserloot On Sat, Nov 27, 2010 at 12:54 PM, Stephen Colebourne wrote: > On 26 November 2010 17:13, Mark Mahieu wrote: > > On 26 Nov 2010, at 10:58, Doug Lea wrote: > >> The workarounds like using 1-slot arrays add a level of > >> indirection to subvert the shallow-only provision. > >> Which seems tolerable. > > > > It's tolerable if the workaround is employed with a good understanding of > its limitations, but it has become a common approach to simple > inconveniences with final variables used in relatively innocuous contexts. > My fear is that it's a workaround which will find itself used in far less > tolerant situations through the simple virtues of being well known and long > established. > > Agreed. And there is a connection to generics wildcards too. In both > cases, restrictions were/are added to the language for good reasons - > to keep the language safe. (For generics I'm thinking of casting a > List to a List, and many other cases as in the huge > FAQ). What happens is: > - the developer tries to perform the "bad" thing > - the compiler stops them > - the developer curses the compiler > - the developer finds the quickest hack > In my experience, most don't worry about WHY the thing was "bad" they > just learn the hacks (and aren't worried if the hack is safe). For > generics, that might be using a raw type. For no access to mutable > variables, its the length one array. > > While I can entirely see why the mutable variable restriction is > desirable from a correctness POV, I'm far from convinced that it is > beneficial from a practical POV, and may actually be harmful (because > only a fraction of developers understand the restriction). As such, > allowing mutable access may well be better overall. Although, some > kind of middle ground like Doug suggests would be good. > > (Note that it could be argued based on the above, that mutable access > via arrays should be blocked to force an object based workaround like > AtomicRefs) > > Stephen > > From thomas.andreas.jung at googlemail.com Sat Nov 27 08:52:31 2010 From: thomas.andreas.jung at googlemail.com (Thomas Jung) Date: Sat, 27 Nov 2010 17:52:31 +0100 Subject: SAM type conversion does not work with inner class Message-ID: Hi, this class does not compile. When C is defined as a static class or an interface it does. public class E{ public void e(){ C c = #{"y"}; } abstract class C{ abstract String x(); } } java.lang.AssertionError at com.sun.tools.javac.code.Symbol.clone(Symbol.java:144) at com.sun.tools.javac.jvm.Gen.binaryQualifier(Gen.java:299) at com.sun.tools.javac.jvm.Gen.visitIdent(Gen.java:2165) at com.sun.tools.javac.tree.JCTree$JCIdent.accept(JCTree.java:1835) at com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:840) at com.sun.tools.javac.jvm.Gen.visitUnary(Gen.java:1894) at com.sun.tools.javac.tree.JCTree$JCUnary.accept(JCTree.java:1624) at com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:840) at com.sun.tools.javac.jvm.Gen.genArgs(Gen.java:859) at com.sun.tools.javac.jvm.Gen.visitApply(Gen.java:1699) at com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java:1344) at com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:840) at com.sun.tools.javac.jvm.Gen.visitExec(Gen.java:1646) at com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1187) at com.sun.tools.javac.jvm.Gen.genDef(Gen.java:687) at com.sun.tools.javac.jvm.Gen.genStat(Gen.java:722) at com.sun.tools.javac.jvm.Gen.genStat(Gen.java:708) at com.sun.tools.javac.jvm.Gen.genStats(Gen.java:759) at com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:1035) at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:801) at com.sun.tools.javac.jvm.Gen.genDef(Gen.java:687) at com.sun.tools.javac.jvm.Gen.genStat(Gen.java:722) at com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:916) at com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:888) at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:684) at com.sun.tools.javac.jvm.Gen.genDef(Gen.java:687) at com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2369) at com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:691) at com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1419) at com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1387) at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:843) at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:802) at com.sun.tools.javac.main.Main.compile(Main.java:418) at com.sun.tools.javac.main.Main.compile(Main.java:336) at com.sun.tools.javac.main.Main.compile(Main.java:327) at com.sun.tools.javac.Main.compile(Main.java:100) Thomas From dl at cs.oswego.edu Sat Nov 27 09:25:38 2010 From: dl at cs.oswego.edu (Doug Lea) Date: Sat, 27 Nov 2010 12:25:38 -0500 Subject: pure functions and the parallel evaluation of reduce-like operations In-Reply-To: References: Message-ID: <4CF13F12.8090400@cs.oswego.edu> On 11/26/10 21:39, Jim Mayer wrote: > In the recent debate on capturing mutable local variables the "reduce" > function was used as an example application of functional programming, > sometimes with an implication that the form could be evaluated in parallel. > It seems to me, though, that a parallel evaluation scheme is only > applicable if the operator is associative (or, better, commutative and > associative). The applicability mainly depends on how deterministic you require your program to be. For example, unless you use "strictfp" (which I don't recall seeing recently in applications) even sequential floating point calculations aren't necessary replicable across platforms or runs, and parallel versions would often require even more care or tolerance. Also, performance-wise, there is almost nothing deterministic about a given Java program, and potential parallelism is sure to make performance estimation even harder. It is an interesting empirical question how much less determinism of both computations and performance typical programs will tolerate. (BTW, for some fun and extreme views of this, see some of Martin Rinard's papers and talks. Although google doesn't find any videos of them.) -Doug From dl at cs.oswego.edu Sat Nov 27 10:36:16 2010 From: dl at cs.oswego.edu (Doug Lea) Date: Sat, 27 Nov 2010 13:36:16 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> <4CE97E88.5010803@bothner.com> <4CEF92CF.8090600@cs.oswego.edu> Message-ID: <4CF14FA0.8040302@cs.oswego.edu> On 11/27/10 06:54, Stephen Colebourne wrote: > While I can entirely see why the mutable variable restriction is > desirable from a correctness POV, I'm far from convinced that it is > beneficial from a practical POV. When those of us writing underlying core library support cannot rely on locality properties, it means that all the application code that uses it is also suspect, which might be a practical problem :-) > > (Note that it could be argued based on the above, that mutable access > via arrays should be blocked to force an object based workaround like > AtomicRefs) Mostly as an aside: While the borderlines are fuzzy, concurrent programming is mainly about synchronization, and parallel programming is mainly about isolation. AtomicRefs and the like should be rarely applicable in constructions oriented toward parallel execution. As I mentioned, annotations causing translation into ThreadLocals seems plausible for scalars, but for arrays you'd probably prefer some sort of wrapper that causes the program to blow up if any element is ever accessed by any thread other than its creator. -Doug From serge.boulay at gmail.com Sat Nov 27 11:26:12 2010 From: serge.boulay at gmail.com (Serge Boulay) Date: Sat, 27 Nov 2010 14:26:12 -0500 Subject: capturing (or not) mutable local variables In-Reply-To: References: <6BD37934-7BC2-4B66-9001-BC40D3A5622D@oracle.com> <03D40340-3C94-40EF-AE18-C586D8EB42F8@gmail.com> <4CE97E88.5010803@bothner.com> <4CEF92CF.8090600@cs.oswego.edu> Message-ID: At some point I'd like to see a still visible but less convoluted way of doing this, for example by adding a keyword that can be applied to a local" What about the @Shared annotation in "closures for Java" specification? A mandatory warning is required if some local variable or parameter from an enclosing scope is used, unless one of the following conditions holds: 1. The variable is declared final, or 2. The variable is not the target of any assignment (i.e., it is only initialized in its declaration), or 3. The variable is annotated @Shared, or 4. some enclosing construct is annotated @SuppressWarnings("shared"). RE: Reiner Zwitserloot The difference between warning and error is virtually non-existent. As under the hood there will be an enormous difference, keyword is a better fit. Also, with the annotation you can't mark the local 'volatile'. On Nov 27, 2010 6:23 PM, "Serge Boulay" wrote: > "At some point I'd like to see a still visible but less convoluted way of > doing this, for example by adding a keyword that can be applied to a local" > > What about the @Shared annotation in "closures for Java" specification? > > > A mandatory warning is required if some local variable or parameter from an > enclosing scope is used, unless one of the following conditions holds: > > 1. The variable is declared final, or > 2. The variable is not the target of any assignment (i.e., it is only > initialized in its declaration), or > 3. The variable is annotated @Shared, or > 4. some enclosing construct is annotated @SuppressWarnings("shared"). - Show quoted text - On Sat, Nov 27, 2010 at 11:44 AM, Reinier Zwitserloot < reinier at zwitserloot.com> wrote: > That sounds a bit defeatist! > > Just because a sizable chunk of programmers casually write code that > includes an active bug or is extremely fragile doesn't mean we should just > throw in the towel. > > Work should perhaps be done on the warning / error messages. For example, > include a URL to a page that serves as a tutorial for the more complex java > rules, such as why you can't assign a List to a List. But, > that's not a job for lambda-dev, and doesn't have to be done right now. The > exact wording of error messages aren't part of the java spec, fortunately. > > I think PHP shows what happens when one always takes the most direct > pragmatic solution to any problem. I assume I don't have to elaborate much > on why PHP indicates this is not a particularly wise move. > > There's also a decent chance someone with absolutely no understanding as to > why mutables in (non-thread-local) closures are a bad idea will > nevertheless > be coached into doing the right thing. The error message will doubtlessly > reference the notion that _final_ locals _can_ be accessed in the closure. > Making a copy final is a lot simpler than the array/AtomicX workaround, and > so happens to be the superior solution too. > > A second benefit is that, while the one that wrote the code may well be > clueless about what multicore implies, someone else reviewing this code > will > have an easier time identifying what's going on. This is quite java-esque - > a field ref can't possibly invoke code. A local variable can't possibly be > involved in a race condition. It's the basis upon which more complex > reasoning can be performed. An 1-size array/AtomicX is an easily visible > red > flag. > > At some point I'd like to see a still visible but less convoluted way of > doing this, for example by adding a keyword that can be applied to a local, > but there's no particular need to do this for the first release of closures > in java. An advantage of doing it later is that there will be far more > experience to base further language changes on. > > --Reinier Zwitserloot > > > > On Sat, Nov 27, 2010 at 12:54 PM, Stephen Colebourne > wrote: > > > On 26 November 2010 17:13, Mark Mahieu wrote: > > > On 26 Nov 2010, at 10:58, Doug Lea wrote: > > >> The workarounds like using 1-slot arrays add a level of > > >> indirection to subvert the shallow-only provision. > > >> Which seems tolerable. > > > > > > It's tolerable if the workaround is employed with a good understanding > of > > its limitations, but it has become a common approach to simple > > inconveniences with final variables used in relatively innocuous > contexts. > > My fear is that it's a workaround which will find itself used in far > less > > tolerant situations through the simple virtues of being well known and > long > > established. > > > > Agreed. And there is a connection to generics wildcards too. In both > > cases, restrictions were/are added to the language for good reasons - > > to keep the language safe. (For generics I'm thinking of casting a > > List to a List, and many other cases as in the huge > > FAQ). What happens is: > > - the developer tries to perform the "bad" thing > > - the compiler stops them > > - the developer curses the compiler > > - the developer finds the quickest hack > > In my experience, most don't worry about WHY the thing was "bad" they > > just learn the hacks (and aren't worried if the hack is safe). For > > generics, that might be using a raw type. For no access to mutable > > variables, its the length one array. > > > > While I can entirely see why the mutable variable restriction is > > desirable from a correctness POV, I'm far from convinced that it is > > beneficial from a practical POV, and may actually be harmful (because > > only a fraction of developers understand the restriction). As such, > > allowing mutable access may well be better overall. Although, some > > kind of middle ground like Doug suggests would be good. > > > > (Note that it could be argued based on the above, that mutable access > > via arrays should be blocked to force an object based workaround like > > AtomicRefs) > > > > Stephen > > > > > > From jim at pentastich.org Sat Nov 27 14:10:17 2010 From: jim at pentastich.org (Jim Mayer) Date: Sat, 27 Nov 2010 17:10:17 -0500 Subject: pure functions and the parallel evaluation of reduce-like operations In-Reply-To: References: <4CF13F12.8090400@cs.oswego.edu> Message-ID: Doug, Well, I suspect that 99 percent of programmers will neither know not care how their floating point calculations are ordered. I'm also quite sure that anyone trying to invert an ill conditioned matrix will care a lot. But that's just for number crunching, and addition and multiplication are wonderfully tractable operations. What about functions that are not associative at all? Then it is not a matter of determinism but of correctness. Considered List.reduce (#{s, v -> s * s + v}). This is a pure function but is not associative. (1^ 2 +2)^ 2 +3 is 12, but 1^ 2 +(2^ 2 + 3) is 8. I also suspect that some operations will be slowed down by parallel execution because they are gated by the memory subsystem and not the CPU. My point is that we won't be able to just turn existing functions just into parallel versions of themselves, and that for operations like reduce/fold/etc., which are incredibly useful even in single threaded environments, we may well end up with multiple versions with differing degrees of allowed parallelism. -- Jim On Nov 27, 2010 12:28 PM, "Doug Lea"
wrote: From maurizio.cimadamore at oracle.com Sat Nov 27 15:58:05 2010 From: maurizio.cimadamore at oracle.com (maurizio cimadamore) Date: Sat, 27 Nov 2010 23:58:05 +0000 Subject: SAM type conversion does not work with inner class In-Reply-To: References: Message-ID: <4CF19B0D.2080709@oracle.com> Thanks Thomas I will look into it Maurizio On 27/11/2010 16:52, Thomas Jung wrote: > Hi, > > this class does not compile. When C is defined as a static class or an > interface it does. > > public class E{ > public void e(){ > C c = #{"y"}; > } > abstract class C{ > abstract String x(); > } > } > > java.lang.AssertionError > at com.sun.tools.javac.code.Symbol.clone(Symbol.java:144) > at com.sun.tools.javac.jvm.Gen.binaryQualifier(Gen.java:299) > at com.sun.tools.javac.jvm.Gen.visitIdent(Gen.java:2165) > at com.sun.tools.javac.tree.JCTree$JCIdent.accept(JCTree.java:1835) > at com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:840) > at com.sun.tools.javac.jvm.Gen.visitUnary(Gen.java:1894) > at com.sun.tools.javac.tree.JCTree$JCUnary.accept(JCTree.java:1624) > at com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:840) > at com.sun.tools.javac.jvm.Gen.genArgs(Gen.java:859) > at com.sun.tools.javac.jvm.Gen.visitApply(Gen.java:1699) > at com.sun.tools.javac.tree.JCTree$JCMethodInvocation.accept(JCTree.java:1344) > at com.sun.tools.javac.jvm.Gen.genExpr(Gen.java:840) > at com.sun.tools.javac.jvm.Gen.visitExec(Gen.java:1646) > at com.sun.tools.javac.tree.JCTree$JCExpressionStatement.accept(JCTree.java:1187) > at com.sun.tools.javac.jvm.Gen.genDef(Gen.java:687) > at com.sun.tools.javac.jvm.Gen.genStat(Gen.java:722) > at com.sun.tools.javac.jvm.Gen.genStat(Gen.java:708) > at com.sun.tools.javac.jvm.Gen.genStats(Gen.java:759) > at com.sun.tools.javac.jvm.Gen.visitBlock(Gen.java:1035) > at com.sun.tools.javac.tree.JCTree$JCBlock.accept(JCTree.java:801) > at com.sun.tools.javac.jvm.Gen.genDef(Gen.java:687) > at com.sun.tools.javac.jvm.Gen.genStat(Gen.java:722) > at com.sun.tools.javac.jvm.Gen.genMethod(Gen.java:916) > at com.sun.tools.javac.jvm.Gen.visitMethodDef(Gen.java:888) > at com.sun.tools.javac.tree.JCTree$JCMethodDecl.accept(JCTree.java:684) > at com.sun.tools.javac.jvm.Gen.genDef(Gen.java:687) > at com.sun.tools.javac.jvm.Gen.genClass(Gen.java:2369) > at com.sun.tools.javac.main.JavaCompiler.genCode(JavaCompiler.java:691) > at com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1419) > at com.sun.tools.javac.main.JavaCompiler.generate(JavaCompiler.java:1387) > at com.sun.tools.javac.main.JavaCompiler.compile2(JavaCompiler.java:843) > at com.sun.tools.javac.main.JavaCompiler.compile(JavaCompiler.java:802) > at com.sun.tools.javac.main.Main.compile(Main.java:418) > at com.sun.tools.javac.main.Main.compile(Main.java:336) > at com.sun.tools.javac.main.Main.compile(Main.java:327) > at com.sun.tools.javac.Main.compile(Main.java:100) > > Thomas > From brian.goetz at oracle.com Sat Nov 27 17:26:59 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 27 Nov 2010 20:26:59 -0500 Subject: pure functions and the parallel evaluation of reduce-like operations In-Reply-To: References: Message-ID: <4265AE3A-0D49-431C-873C-519DA13394B1@oracle.com> > Do we define annotations to declare intent (e.g, @Associative)? Do we have > multiple reduce operations (e.g., leftReduce, rightReduce, > associativeReduce, unorderedReduce)? Do we pass in the function's > constraints as a parameter (e.g., List.reduce(Operation.ASSOCIATIVE, #{s v > -> s + v}))? This sort of thing would be a good match for Mike Ernst's work on pluggable type systems via JSR 308 and the "checkers" framework. From brian.goetz at oracle.com Sat Nov 27 17:45:09 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Sat, 27 Nov 2010 20:45:09 -0500 Subject: pure functions and the parallel evaluation of reduce-like operations In-Reply-To: References: Message-ID: <6BD6BE02-039A-4334-9C3F-A3DF6750ACA9@oracle.com> > Do we define annotations to declare intent (e.g, @Associative)? Do we have > multiple reduce operations (e.g., leftReduce, rightReduce, > associativeReduce, unorderedReduce)? Do we pass in the function's > constraints as a parameter (e.g., List.reduce(Operation.ASSOCIATIVE, #{s v > -> s + v}))? This sort of thing would be a good match for Mike Ernst's work on pluggable type systems via JSR 308 and the "checkers" framework. From forax at univ-mlv.fr Sun Nov 28 06:45:16 2010 From: forax at univ-mlv.fr (=?ISO-8859-1?Q?R=E9mi_Forax?=) Date: Sun, 28 Nov 2010 15:45:16 +0100 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> Message-ID: <4CF26AFC.50000@univ-mlv.fr> On 11/25/2010 02:22 AM, Neal Gafter wrote: >>> Anyway, the good news is that mod-extn _is_ SC. That's true whether a >>> default is specified in the interface via a method body or a method name. >>> >> Should have said: mod-extn _may be_ SC. When an implementation class >> doesn't override the extension, it's possible for previously-identical >> extension methods to diverge (i.e. one changes its default) and break SC. >> > Understood. That is the particular part of the specification that I take > issue with. mod-extn (changing the code of a default method) should (in my > opinion) always be source compatible, as the code of a method is a prime > example of an implementation detail. > There is perhaps a way to satisfy both parties :) I'm not in favor to use the default methods to disambiguate when there are several extension methods. As Neal said, the default method is part of the implementation. I propose to use the declaring type instead of the default method to disambiguate. Let's take several examples: interface Foo { extension void m() default Foos.m; } interface Bar { extension void m() default Bars.m; } class A implements Foo, Bar { } A::m can be Foo::m or Bar::m, because there is no most specific type between Foo and Bar, a call to A::m should throw a linkage error. interface Foo { extension void m() default Foos.m; } interface Bar extends Foo { extension void m() default Bars.m; } class A implements Foo, Bar { } A::m can be Foo::m or Bar::m, because Bar is a sutype of Foo, a call to A::m will call the default method of Bar::m, Bars.m. interface Foo { extension void m() default Foos.m; } interface Bar extends Foo {} interface Baz extends Foo { } class A implements Bar, Baz { } A::m can be Foo::m or Foo:m, so a call to A::m will call Foos.m. R?mi From brian.goetz at oracle.com Sun Nov 28 16:23:32 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 28 Nov 2010 19:23:32 -0500 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> Message-ID: <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> >>> Anyway, the good news is that mod-extn _is_ SC. That's true whether a >>> default is specified in the interface via a method body or a method name. >>> >> >> Should have said: mod-extn _may be_ SC. When an implementation class >> doesn't override the extension, it's possible for previously-identical >> extension methods to diverge (i.e. one changes its default) and break SC. >> > > Understood. That is the particular part of the specification that I take > issue with. mod-extn (changing the code of a default method) should (in my > opinion) always be source compatible, as the code of a method is a prime > example of an implementation detail. Sorry for the delay in responding on this: have been on holiday. The code of the default method *is* an implementation detail. However, the identity of the default method is not. You can consider it to be a new dimension of the signature, if you like; it is part of the inheritance metadata. I can see why you would want to consider it an implementation detail -- I wanted to initially as well -- but that's not really how this feature works. But the limitations on SC are not new. We're used to having certain "subject to" caveats in the definition of SC. For example, add-meth is SC, but in reality only to a point: if the newly added method would cause an illegal override in a subclass, then it is not SC, but this a minor enough consideration to be swept under the rug via an asterisk and we consider add-meth to be source compatible. (Note that the conflict may be with an implementation not known to the class being modified.) The exact same sort of caveat is in operation for mod-extn. mod-extn is source-comptable, except when it causes a conflict with a subclass that inherits the method *and* the same method with a different default from another (unknown to the modified interface) interface. While this sort of conflict may well be more likely than the sort in the previous example (or may not, we're not sure yet), it is definitely of the same flavor. From brian.goetz at oracle.com Sun Nov 28 16:35:17 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 28 Nov 2010 19:35:17 -0500 Subject: Defender methods and compatibility In-Reply-To: <4CF26AFC.50000@univ-mlv.fr> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <4CF26AFC.50000@univ-mlv.fr> Message-ID: <7AA11239-E371-43BE-9AB2-6E1522603E39@oracle.com> > I'm not in favor to use the default methods to disambiguate when > there are several extension methods. As Neal said, the default method > is part of the implementation. I disagree with Neal's interpretation. I think the body of the default is an implementation detail, but the identity of the default is not. > I propose to use the declaring type instead of the default method > to disambiguate. Your rules below all produce the same results as the currently described rules. Can you offer an example where your rules produce a different result? > Let's take several examples: > > interface Foo { > extension void m() default Foos.m; > } > interface Bar { > extension void m() default Bars.m; > } > class A implements Foo, Bar { } > > A::m can be Foo::m or Bar::m, because > there is no most specific type between Foo and Bar, > a call to A::m should throw a linkage error. > > interface Foo { > extension void m() default Foos.m; > } > interface Bar extends Foo { > extension void m() default Bars.m; > } > class A implements Foo, Bar { } > > A::m can be Foo::m or Bar::m, because > Bar is a sutype of Foo, a call to A::m will > call the default method of Bar::m, Bars.m. > > interface Foo { > extension void m() default Foos.m; > } > interface Bar extends Foo {} > interface Baz extends Foo { } > class A implements Bar, Baz { } > > A::m can be Foo::m or Foo:m, so a call to > A::m will call Foos.m. > > R?mi > > From isidore at setgame.com Sun Nov 28 16:48:09 2010 From: isidore at setgame.com (Llewellyn Falco) Date: Sun, 28 Nov 2010 16:48:09 -0800 Subject: Defender methods and compatibility In-Reply-To: <7AA11239-E371-43BE-9AB2-6E1522603E39@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <4CF26AFC.50000@univ-mlv.fr> <7AA11239-E371-43BE-9AB2-6E1522603E39@oracle.com> Message-ID: sorry, i know this must sound like a newbie question, but I am confused as to the whole point of the default and defender methods for interfaces. I've been following the thread for a while now, but still can't seem to get my head around it. As far as I can tell, this allows you to have Interfaces that act like abstract objects. Which is confusing, why not just have abstract objects? the 1 possible difference I can see is this would allow for multiple inheritance. and that leads to a very odd question of "why would you want to bring back multiple inheritance?" is there something I am missing here? llewellyn From brian.goetz at oracle.com Sun Nov 28 16:55:36 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 28 Nov 2010 19:55:36 -0500 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <4CF26AFC.50000@univ-mlv.fr> <7AA11239-E371-43BE-9AB2-6E1522603E39@oracle.com> Message-ID: <61537F28-B05A-4EDA-856B-3244A8CCBB30@oracle.com> First, remember that Java already has multiple inheritance -- of types (via interfaces) as well as the contract (methods) for those types. And this is considered one of Java's good features. The goal is: allow for evolution of types defined by interfaces without breaking existing implementations. An example would be adding a "forEach(lambda)" method to Collection. Currently we could add such a method to Collection, but the cost would be high: breaking all implementation classes (or at least all those that don't extend JDK implementations, such as AbstractCollection.) We like the existing multiple inheritance in Java; it is far more flexible than the single inheritance of classes. The limitation is that you can't put state or behavior in interfaces, just interface contacts. Extension methods lifts the restriction about behavior, but not about state. This enables interface evolution, among other things. Much of the practical trouble with multiple inheritance in languages like C++ stem from state and from non-virtual methods. Java doesn't have the latter. Extension methods does not introduce the former. It is very easy to say "multiple inheritance bad", but this is a simplistic view of the world. (Most Java developers don't even realize that they are working with multiple inheritance every day.) The goal here is to find a point that admits as much of the good while preventing most of the bad. Obviously there's some room for disagreement on where that line is. On Nov 28, 2010, at 7:48 PM, Llewellyn Falco wrote: > sorry, i know this must sound like a newbie question, but I am confused as to the whole point of the default and defender methods for interfaces. > > I've been following the thread for a while now, but still can't seem to get my head around it. > > As far as I can tell, this allows you to have Interfaces that act like abstract objects. > Which is confusing, why not just have abstract objects? > > the 1 possible difference I can see is this would allow for multiple inheritance. > and that leads to a very odd question of > > "why would you want to bring back multiple inheritance?" > > > is there something I am missing here? > > llewellyn From neal at gafter.com Sun Nov 28 19:50:37 2010 From: neal at gafter.com (Neal Gafter) Date: Sun, 28 Nov 2010 19:50:37 -0800 Subject: Defender methods and compatibility In-Reply-To: <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> Message-ID: On Sun, Nov 28, 2010 at 4:23 PM, Brian Goetz wrote: > >>> Anyway, the good news is that mod-extn _is_ SC. That's true whether a > >>> default is specified in the interface via a method body or a method > name. > >>> > >> > >> Should have said: mod-extn _may be_ SC. When an implementation class > >> doesn't override the extension, it's possible for previously-identical > >> extension methods to diverge (i.e. one changes its default) and break > SC. > >> > > > > Understood. That is the particular part of the specification that I take > > issue with. mod-extn (changing the code of a default method) should (in > my > > opinion) always be source compatible, as the code of a method is a prime > > example of an implementation detail. > > Sorry for the delay in responding on this: have been on holiday. > > The code of the default method *is* an implementation detail. However, the > identity of the default method is not. You can consider it to be a new > dimension of the signature, if you like; it is part of the inheritance > metadata. I can see why you would want to consider it an implementation > detail -- I wanted to initially as well -- but that's not really how this > feature works. > It is clear that's not how it works, and that's the problem with the current specification. The default shouldn't have any concept of "identity" separate than that of the method itself, nor should the specification add a new way for compatibility to be broken. Otherwise we're introducing a new mechanism for making interfaces more flexible, but introducing a new way to break source compatibility that makes it far less flexible than it could be. We don't want API designers to have to worry about changing a defender method's default in a new version of an API. Such a change should be clearly, obviously, and always source and binary compatible, and API designers should have the freedom to make such changes from version to version without such breakage. As a side benefit, such an improved specification enables the default implementation to be written inline, instead of indirectly, reducing the syntactic footprint of the language change. But the limitations on SC are not new. We're used to having certain > "subject to" caveats in the definition of SC. For example, add-meth is SC, > but in reality only to a point: if the newly added method would cause an > illegal override in a subclass, then it is not SC, but this a minor enough > consideration to be swept under the rug via an asterisk and we consider > add-meth to be source compatible. (Note that the conflict may be with an > implementation not known to the class being modified.) > Right. Adding methods to APIs has always been subject to that kind of breakage, even before this proposed language change. It would be hard to imagine this language change making that any better. > The exact same sort of caveat is in operation for mod-extn. mod-extn is > source-comptable, except when it causes a conflict with a subclass that > inherits the method *and* the same method with a different default from > another (unknown to the modified interface) interface. While this sort of > conflict may well be more likely than the sort in the previous example (or > may not, we're not sure yet), it is definitely of the same flavor. > It isn't the exact same sort of caveat: here the proposal adds a new kind of incompatibility previously absent from the language. Changing a method's implementation (e.g. its default implementation) has never previously been a source incompatible change, and for very good reasons. The software engineering principles that led to and are supported by the current language design appear to have been ignored. If you consider this new kind of source compatibility issue to be in the noise, then you probably also consider the questionable benefits of treating the defaults as having some sort of identity also to be in the noise. In that case, you should prefer a solution that preserves source compatibility. From brian.goetz at oracle.com Sun Nov 28 20:28:08 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Sun, 28 Nov 2010 23:28:08 -0500 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> Message-ID: <4CF32BD8.5050400@oracle.com> > It is clear that's not how it works, and that's the problem with the current > specification. The default shouldn't have any concept of "identity" separate > than that of the method itself, nor should the specification add a new way for > compatibility to be broken. You keep using the word "should" as if there is a a categorical principle here, but I don't see it. I have proposed a design for a language feature. You would prefer a similar but slightly different feature. Not surprising, there's more than one way to skin a cat. (Further, I think you're making a lot of noise about a corner case. Collisions are likely to be rare. In the vast majority of cases, API designers will be able to make changes to defaults without breakage.) I welcome your opinion but you will need to do a lot better than just chanting "should, wrong, broken" if you want to influence the outcome. > The exact same sort of caveat is in operation for mod-extn. mod-extn is > source-comptable, except when it causes a conflict with a subclass that > inherits the method *and* the same method with a different default from > another (unknown to the modified interface) interface. While this sort of > conflict may well be more likely than the sort in the previous example (or > may not, we're not sure yet), it is definitely of the same flavor. > > It isn't the exact same sort of caveat: here the proposal adds a new kind of > incompatibility previously absent from the language. Changing a method's > implementation (e.g. its default implementation) Objection, assumes facts not in evidence. You keep assuming that the identity of the default is an implementation detail, and then state that as fact. You are incorrect, it is not an implementation detail. The *body* of the method is, as method bodies always have been, an implementation detail. But the best way to think about the default is that it is part of the signature. From forax at univ-mlv.fr Mon Nov 29 01:56:29 2010 From: forax at univ-mlv.fr (=?ISO-8859-1?Q?R=E9mi_Forax?=) Date: Mon, 29 Nov 2010 10:56:29 +0100 Subject: Defender methods and compatibility In-Reply-To: <7AA11239-E371-43BE-9AB2-6E1522603E39@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <4CF26AFC.50000@univ-mlv.fr> <7AA11239-E371-43BE-9AB2-6E1522603E39@oracle.com> Message-ID: <4CF378CD.2060608@univ-mlv.fr> Le 29/11/2010 01:35, Brian Goetz a ?crit : [...] >> I propose to use the declaring type instead of the default method >> to disambiguate. > Your rules below all produce the same results as the currently described rules. Can you offer an example where your rules produce a different result? No I can't. In fact, I hope that the semantics are equivalent. The current spec mandates that first type of the default method is the declaring type of the interface, so considering that type or the default method is equivalent. R?mi From maurizio.cimadamore at oracle.com Mon Nov 29 04:08:56 2010 From: maurizio.cimadamore at oracle.com (maurizio.cimadamore at oracle.com) Date: Mon, 29 Nov 2010 12:08:56 +0000 Subject: hg: lambda/lambda/langtools: Compiler generates bad code when SAM-converting a lambda into an inner (abstract) class. Message-ID: <20101129120901.D4D7B47ECB@hg.openjdk.java.net> Changeset: 83c22f23d80c Author: mcimadamore Date: 2010-11-29 12:04 +0000 URL: http://hg.openjdk.java.net/lambda/lambda/langtools/rev/83c22f23d80c Compiler generates bad code when SAM-converting a lambda into an inner (abstract) class. ! src/share/classes/com/sun/tools/javac/comp/Unlambda.java + test/tools/javac/lambda/LambdaConv14.java From jim at pentastich.org Mon Nov 29 06:02:15 2010 From: jim at pentastich.org (Jim Mayer) Date: Mon, 29 Nov 2010 09:02:15 -0500 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> Message-ID: Neal and Brian, Am I right that there are two partially separable issues that Neal is bringing up? (1) that Neal would like to remove the rule that allows extension methods to be defined in two super-interfaces of a class without explicit disambiguation if the two interfaces specify the same default. For example: interface I { extension void m() default X.a; } interface J { extension void m() default X.a; } class C implements I, J {} and (2) that Neal would like to be able to specify the "default" in-line (with (1) being a prerequisite for this). If that's correct, then I'm going to ignore the issue of in-line specification and just focus on (1) (if incorrect, then I'm going to type gobbledygook ). My reading of Neal's argument is that he would like the act of selecting a different default method for an extension (mod-extn) to be source compatible. To get this property, he's willing to push some of the risk of breaking source compatibility back on to the act of adding an extension (add-extn). The justification for this is that "add-extn" cannot be source compatible anyway, and that removing the rule gives an absolute guarantee of source compatibility for "mod-extn". Making this change to the draft specification would make adding identical extension methods with identical defaults to a set of interfaces that are likely to be implemented by a single class harder because they would be more likely to trigger a compile error. I have no clue as to how often this would happen in practice. My questions for Brian, then, are: 1. What are the specific use cases where being able to specify the same default implementation in multiple definitions would make a significant difference to the ability to extend an API. 2. In those cases, are there any workarounds available (e.g., putting the common definition of the method into its own interface). Thanks! -- Jim On Sun, Nov 28, 2010 at 10:50 PM, Neal Gafter wrote: > On Sun, Nov 28, 2010 at 4:23 PM, Brian Goetz > wrote: > > > >>> Anyway, the good news is that mod-extn _is_ SC. That's true whether a > > >>> default is specified in the interface via a method body or a method > > name. > > >>> > > >> > > >> Should have said: mod-extn _may be_ SC. When an implementation class > > >> doesn't override the extension, it's possible for previously-identical > > >> extension methods to diverge (i.e. one changes its default) and break > > SC. > > >> > > > > > > Understood. That is the particular part of the specification that I > take > > > issue with. mod-extn (changing the code of a default method) should > (in > > my > > > opinion) always be source compatible, as the code of a method is a > prime > > > example of an implementation detail. > > > > Sorry for the delay in responding on this: have been on holiday. > > > > The code of the default method *is* an implementation detail. However, > the > > identity of the default method is not. You can consider it to be a new > > dimension of the signature, if you like; it is part of the inheritance > > metadata. I can see why you would want to consider it an implementation > > detail -- I wanted to initially as well -- but that's not really how this > > feature works. > > > > It is clear that's not how it works, and that's the problem with the > current > specification. The default shouldn't have any concept of "identity" > separate than that of the method itself, nor should the specification add a > new way for compatibility to be broken. Otherwise we're introducing a new > mechanism for making interfaces more flexible, but introducing a new way to > break source compatibility that makes it far less flexible than it could > be. We don't want API designers to have to worry about changing a defender > method's default in a new version of an API. Such a change should be > clearly, obviously, and always source and binary compatible, and API > designers should have the freedom to make such changes from version to > version without such breakage. > > As a side benefit, such an improved specification enables the default > implementation to be written inline, instead of indirectly, reducing the > syntactic footprint of the language change. > > But the limitations on SC are not new. We're used to having certain > > "subject to" caveats in the definition of SC. For example, add-meth is > SC, > > but in reality only to a point: if the newly added method would cause an > > illegal override in a subclass, then it is not SC, but this a minor > enough > > consideration to be swept under the rug via an asterisk and we consider > > add-meth to be source compatible. (Note that the conflict may be with an > > implementation not known to the class being modified.) > > > > Right. Adding methods to APIs has always been subject to that kind of > breakage, even before this proposed language change. It would be hard to > imagine this language change making that any better. > > > > The exact same sort of caveat is in operation for mod-extn. mod-extn is > > source-comptable, except when it causes a conflict with a subclass that > > inherits the method *and* the same method with a different default from > > another (unknown to the modified interface) interface. While this sort > of > > conflict may well be more likely than the sort in the previous example > (or > > may not, we're not sure yet), it is definitely of the same flavor. > > > > It isn't the exact same sort of caveat: here the proposal adds a new kind > of > incompatibility previously absent from the language. Changing a method's > implementation (e.g. its default implementation) has never previously been > a > source incompatible change, and for very good reasons. The software > engineering principles that led to and are supported by the current > language > design appear to have been ignored. > > If you consider this new kind of source compatibility issue to be in the > noise, then you probably also consider the questionable benefits of > treating > the defaults as having some sort of identity also to be in the noise. In > that case, you should prefer a solution that preserves source > compatibility. > > From neal at gafter.com Mon Nov 29 07:00:39 2010 From: neal at gafter.com (Neal Gafter) Date: Mon, 29 Nov 2010 07:00:39 -0800 Subject: Defender methods and compatibility In-Reply-To: <4CF32BD8.5050400@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> Message-ID: On Sunday, November 28, 2010, Brian Goetz wrote: > You keep using the word "should" as if there is a a categorical principle here, but I don't see it. ?I have proposed a design for a language feature. You would prefer a similar but slightly different feature. ?Not surprising, there's more than one way to skin a cat. > > (Further, I think you're making a lot of noise about a corner case. Collisions are likely to be rare. ?In the vast majority of cases, API designers will be able to make changes to defaults without breakage.) > > I welcome your opinion but you will need to do a lot better than just chanting "should, wrong, broken" if you want to influence the outcome. > You keep assuming that the identity of the default is an implementation detail, and then state that as fact. ?You are incorrect, it is not an implementation detail. ?The *body* of the method is, as method bodies always have been, an implementation detail. ?But the best way to think about the default is that it is part of the signature. Perhaps it would help me to understand the current design if you explain the software engineering principles and processes it is intended to support, as the current design seems to me to undermine the more important principle of flexibility in API evolution in favor of the (it seems to me) less important property of automatically resolving ambiguities in some situations when methods that are unrelated in the inheritance hierarchy have colliding signatures. Am I to understand that you value these factors differently? From thomas.andreas.jung at googlemail.com Mon Nov 29 08:04:02 2010 From: thomas.andreas.jung at googlemail.com (Thomas Jung) Date: Mon, 29 Nov 2010 17:04:02 +0100 Subject: hg: lambda/lambda/langtools: Compiler generates bad code when SAM-converting a lambda into an inner (abstract) class. In-Reply-To: <20101129120901.D4D7B47ECB@hg.openjdk.java.net> References: <20101129120901.D4D7B47ECB@hg.openjdk.java.net> Message-ID: +import org.omg.CORBA.INITIALIZE; Corba - it's everywhere. :-) On 29 November 2010 13:08, wrote: > Changeset: 83c22f23d80c > Author: ? ?mcimadamore > Date: ? ? ?2010-11-29 12:04 +0000 > URL: ? ? ? http://hg.openjdk.java.net/lambda/lambda/langtools/rev/83c22f23d80c > > Compiler generates bad code when SAM-converting a lambda into an inner (abstract) class. > > ! src/share/classes/com/sun/tools/javac/comp/Unlambda.java > + test/tools/javac/lambda/LambdaConv14.java > > > From maurizio.cimadamore at oracle.com Mon Nov 29 08:56:47 2010 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 29 Nov 2010 16:56:47 +0000 Subject: hg: lambda/lambda/langtools: Compiler generates bad code when SAM-converting a lambda into an inner (abstract) class. In-Reply-To: References: <20101129120901.D4D7B47ECB@hg.openjdk.java.net> Message-ID: <4CF3DB4F.3090301@oracle.com> On 29/11/10 16:04, Thomas Jung wrote: > +import org.omg.CORBA.INITIALIZE; > > Corba - it's everywhere. :-) > :-) The truth is that I want to make the jigsaw folk really angry with me and start adding random dependencies everywhere! Maurizio > > On 29 November 2010 13:08, wrote: > >> Changeset: 83c22f23d80c >> Author: mcimadamore >> Date: 2010-11-29 12:04 +0000 >> URL: http://hg.openjdk.java.net/lambda/lambda/langtools/rev/83c22f23d80c >> >> Compiler generates bad code when SAM-converting a lambda into an inner (abstract) class. >> >> ! src/share/classes/com/sun/tools/javac/comp/Unlambda.java >> + test/tools/javac/lambda/LambdaConv14.java >> >> >> >> > From maurizio.cimadamore at oracle.com Mon Nov 29 08:59:25 2010 From: maurizio.cimadamore at oracle.com (maurizio.cimadamore at oracle.com) Date: Mon, 29 Nov 2010 16:59:25 +0000 Subject: hg: lambda/lambda/langtools: Minor syntactic changes: Message-ID: <20101129165927.84D9E47EEC@hg.openjdk.java.net> Changeset: b635a35cbb89 Author: mcimadamore Date: 2010-11-29 16:58 +0000 URL: http://hg.openjdk.java.net/lambda/lambda/langtools/rev/b635a35cbb89 Minor syntactic changes: *) Lambda must start with the special token '#{' (no separator allowed between '#' and '{') *) 'default' keyword on extension method is now optional (compiler warns you about it) ! src/share/classes/com/sun/tools/javac/comp/Attr.java ! src/share/classes/com/sun/tools/javac/comp/Unlambda.java ! src/share/classes/com/sun/tools/javac/parser/JavacParser.java ! src/share/classes/com/sun/tools/javac/parser/Scanner.java ! src/share/classes/com/sun/tools/javac/parser/Token.java ! src/share/classes/com/sun/tools/javac/resources/compiler.properties ! test/tools/javac/defender/Neg01.java ! test/tools/javac/defender/Neg02.java ! test/tools/javac/diags/examples.not-yet.txt - test/tools/javac/diags/examples/DefaultAllowedInIntfAnnotationMember.java + test/tools/javac/lambda/BadLambdaToken.java + test/tools/javac/lambda/BadLambdaToken.out ! test/tools/javac/lambda/BadOrder.java ! test/tools/javac/lambda/BadReturn.java ! test/tools/javac/lambda/BadReturn.out ! test/tools/javac/lambda/DefiniteAssignment01.java + test/tools/javac/lambda/DeprecatedExtensionKeyword.java + test/tools/javac/lambda/DeprecatedExtensionKeyword.out ! test/tools/javac/lambda/ExceptionTransparency02.java From brian.goetz at oracle.com Mon Nov 29 09:26:02 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Mon, 29 Nov 2010 12:26:02 -0500 Subject: hg: lambda/lambda/langtools: Minor syntactic changes: In-Reply-To: <20101129165927.84D9E47EEC@hg.openjdk.java.net> References: <20101129165927.84D9E47EEC@hg.openjdk.java.net> Message-ID: <4CF3E22A.5010904@oracle.com> > Minor syntactic changes: > *) Lambda must start with the special token '#{' (no separator allowed between '#' and '{') > *) 'default' keyword on extension method is now optional (compiler warns you about it) You mean "extension" keyword, no? From maurizio.cimadamore at oracle.com Mon Nov 29 09:33:39 2010 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Mon, 29 Nov 2010 17:33:39 +0000 Subject: hg: lambda/lambda/langtools: Minor syntactic changes: In-Reply-To: <4CF3E22A.5010904@oracle.com> References: <20101129165927.84D9E47EEC@hg.openjdk.java.net> <4CF3E22A.5010904@oracle.com> Message-ID: <4CF3E3F3.2080206@oracle.com> On 29/11/10 17:26, Brian Goetz wrote: >> Minor syntactic changes: >> *) Lambda must start with the special token '#{' (no separator >> allowed between '#' and '{') >> *) 'default' keyword on extension method is now optional (compiler >> warns you about it) > > You mean "extension" keyword, no? Right 'extension'. Summarizing, the two changes are meant to improve the usability of the lambda compiler. On the one hand we want to avoid stuff like: SAM s = # { //doSomething }; (hence the single lambda token '#{') On the other hand, we want to make extension method declaration more straightforward so that this: interface I { extension m() default Impl.foo; } can become simply: interface I { m() default Impl.foo; } [as warmly suggested on this mailing list ;-) ] Maurizio From reinier at zwitserloot.com Mon Nov 29 09:53:07 2010 From: reinier at zwitserloot.com (Reinier Zwitserloot) Date: Mon, 29 Nov 2010 18:53:07 +0100 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> Message-ID: When is the currently proposed "default method is part of the signature" aspect going to result in a problem? If this is about ideological purity, then I vote we stop painting this bikeshed. --Reinier Zwitserloot On Mon, Nov 29, 2010 at 4:00 PM, Neal Gafter wrote: > On Sunday, November 28, 2010, Brian Goetz wrote: > > You keep using the word "should" as if there is a a categorical principle > here, but I don't see it. I have proposed a design for a language feature. > You would prefer a similar but slightly different feature. Not surprising, > there's more than one way to skin a cat. > > > > (Further, I think you're making a lot of noise about a corner case. > Collisions are likely to be rare. In the vast majority of cases, API > designers will be able to make changes to defaults without breakage.) > > > > I welcome your opinion but you will need to do a lot better than just > chanting "should, wrong, broken" if you want to influence the outcome. > > You keep assuming that the identity of the default is an implementation > detail, and then state that as fact. You are incorrect, it is not an > implementation detail. The *body* of the method is, as method bodies always > have been, an implementation detail. But the best way to think about the > default is that it is part of the signature. > > Perhaps it would help me to understand the current design if you > explain the software engineering principles and processes it is > intended to support, as the current design seems to me to undermine > the more important principle of flexibility in API evolution in favor > of the (it seems to me) less important property of automatically > resolving ambiguities in some situations when methods that are > unrelated in the inheritance hierarchy have colliding signatures. Am > I to understand that you value these factors differently? > > From neal at gafter.com Mon Nov 29 14:28:18 2010 From: neal at gafter.com (Neal Gafter) Date: Mon, 29 Nov 2010 14:28:18 -0800 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> Message-ID: On Mon, Nov 29, 2010 at 9:53 AM, Reinier Zwitserloot < reinier at zwitserloot.com> wrote: > When is the currently proposed "default method is part of the signature" > aspect going to result in a problem? If this is about ideological purity, > then I vote we stop painting this bikeshed. > An interesting reference to Parkinson's *law of triviality*. The basis of Parkinson's law is that people seem to care less about issues they don't understand, no matter how important those issues might be relative to the issues for which they express a preference. "painting this bikeshed" is an *ad hominem *suggestion that the participants in this discussion are really only commenting on those issues simple enough for them to understand. In this case, however, you also appear to be saying that you don't fully appreciate the issue, and therefore don't see any point in continuing the discussion. Which ironically reinforces the reference to Parkinson's law. To be (hopefully) more clear, here are the costs of the current approach: o New syntax for extension methods (as opposed to using the existing method body syntax) o Changing the default of an extension method is not source compatible (in the sense that it can cause some existing clients to no longer compile) o Under the current draft spec, not binary compatible either, but there have been promised (as yet unexplained) complexities to be added to method resolution (and therefore the JVM) that may be introduced to reduce this effect. Here are the benefits: o Certain compile-time ambiguities might be avoided when extension methods in unrelated classes have the same signature and implementation. The practical impact of the source incompatibility is that an API designer cannot change an extension method's default without potentially breaking some clients (i.e. causing them to no longer compile). That is so because the API designer generally does not possess all of the client code of the API to test against to ensure they all still compile. Changing the default implementation is just the sort of change an API designer would want to make from one release to another to, for example, improve performance. But the fact that it is an incompatible change inhibits the API designer from making such changes in the future. As a consequence, the language feature as proposed is more likely to result in future APIs that have more (rather than fewer) artifacts of their history. Historical artifacts are one of the pain points of the Java platform that we'd be better off minimizing. The default of an extension method is not a part of the programmer-visible API in any useful, observable sense *except to the extent that it can introduce this incompatibility.* From forax at univ-mlv.fr Mon Nov 29 15:06:42 2010 From: forax at univ-mlv.fr (=?ISO-8859-1?Q?R=E9mi_Forax?=) Date: Tue, 30 Nov 2010 00:06:42 +0100 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> Message-ID: <4CF43202.3040605@univ-mlv.fr> On 11/29/2010 11:28 PM, Neal Gafter wrote: > On Mon, Nov 29, 2010 at 9:53 AM, Reinier Zwitserloot< > reinier at zwitserloot.com> wrote: > >> When is the currently proposed "default method is part of the signature" >> aspect going to result in a problem? If this is about ideological purity, >> then I vote we stop painting this bikeshed. >> > An interesting reference to Parkinson's *law of triviality*. The basis of > Parkinson's law is that people seem to care less about issues they don't > understand, no matter how important those issues might be relative to the > issues for which they express a preference. "painting this bikeshed" is an > *ad hominem *suggestion that the participants in this discussion are really > only commenting on those issues simple enough for them to understand. > > In this case, however, you also appear to be saying that you don't fully > appreciate the issue, and therefore don't see any point in continuing the > discussion. Which ironically reinforces the reference to Parkinson's law. > > To be (hopefully) more clear, here are the costs of the current approach: > o New syntax for extension methods (as opposed to using the existing method > body syntax) > o Changing the default of an extension method is not source compatible (in > the sense that it can cause some existing clients to no longer compile) > o Under the current draft spec, not binary compatible either, but there have > been promised (as yet unexplained) complexities to be added to method > resolution (and therefore the JVM) that may be introduced to reduce this > effect. > > Here are the benefits: > o Certain compile-time ambiguities might be avoided when extension methods > in unrelated classes have the same signature and implementation. How unrelated classes can have the same default method ? The spec says that the first parameter of a default method is the class containing the extension method. > The practical impact of the source incompatibility is that an API designer > cannot change an extension method's default without potentially breaking > some clients (i.e. causing them to no longer compile). That is so because > the API designer generally does not possess all of the client code of the > API to test against to ensure they all still compile. Changing the default > implementation is just the sort of change an API designer would want to make > from one release to another to, for example, improve performance. But the > fact that it is an incompatible change inhibits the API designer from making > such changes in the future. As a consequence, the language feature as > proposed is more likely to result in future APIs that have more (rather than > fewer) artifacts of their history. Historical artifacts are one of the pain > points of the Java platform that we'd be better off minimizing. > > The default of an extension method is not a part of the programmer-visible > API in any useful, observable sense *except to the extent that it can > introduce this incompatibility.* R?mi From nathan.bryant at linkshare.com Mon Nov 29 15:17:47 2010 From: nathan.bryant at linkshare.com (Nathan Bryant) Date: Tue, 30 Nov 2010 08:17:47 +0900 Subject: Defender methods and compatibility References: <4CEC169B.3040300@oracle.com><4CEC5D4E.4060903@oracle.com><4CEC66C5.3040505@oracle.com><4CED6EF3.5070408@oracle.com><4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com><7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com><4CF32BD8.5050400@oracle.com> Message-ID: <7FDA6630E1822F448C97A48D5D733094028C9558@EXVMSTOR302.intra.rakuten.co.jp> Neal, Maybe I just don't get it. In Brian and Oracle's version, changing the default may be source incompatible, only if the change is to which static method provides the default. Changing the body of that static method should usually be source compatible. In your preferred solution, there is no source compatibility at all, in the case where two superinterfaces wish to provide defenders for the same method. Requiring the implementing class to resolve the ambiguity is not source compatible. Nathan -----Original Message----- From: lambda-dev-bounces at openjdk.java.net [mailto:lambda-dev-bounces at openjdk.java.net] On Behalf Of Neal Gafter Sent: Monday, November 29, 2010 5:28 PM To: Reinier Zwitserloot Cc: lambda-dev at openjdk.java.net Subject: Re: Defender methods and compatibility On Mon, Nov 29, 2010 at 9:53 AM, Reinier Zwitserloot < reinier at zwitserloot.com> wrote: > When is the currently proposed "default method is part of the signature" > aspect going to result in a problem? If this is about ideological purity, > then I vote we stop painting this bikeshed. > An interesting reference to Parkinson's *law of triviality*. The basis of Parkinson's law is that people seem to care less about issues they don't understand, no matter how important those issues might be relative to the issues for which they express a preference. "painting this bikeshed" is an *ad hominem *suggestion that the participants in this discussion are really only commenting on those issues simple enough for them to understand. In this case, however, you also appear to be saying that you don't fully appreciate the issue, and therefore don't see any point in continuing the discussion. Which ironically reinforces the reference to Parkinson's law. To be (hopefully) more clear, here are the costs of the current approach: o New syntax for extension methods (as opposed to using the existing method body syntax) o Changing the default of an extension method is not source compatible (in the sense that it can cause some existing clients to no longer compile) o Under the current draft spec, not binary compatible either, but there have been promised (as yet unexplained) complexities to be added to method resolution (and therefore the JVM) that may be introduced to reduce this effect. Here are the benefits: o Certain compile-time ambiguities might be avoided when extension methods in unrelated classes have the same signature and implementation. The practical impact of the source incompatibility is that an API designer cannot change an extension method's default without potentially breaking some clients (i.e. causing them to no longer compile). That is so because the API designer generally does not possess all of the client code of the API to test against to ensure they all still compile. Changing the default implementation is just the sort of change an API designer would want to make from one release to another to, for example, improve performance. But the fact that it is an incompatible change inhibits the API designer from making such changes in the future. As a consequence, the language feature as proposed is more likely to result in future APIs that have more (rather than fewer) artifacts of their history. Historical artifacts are one of the pain points of the Java platform that we'd be better off minimizing. The default of an extension method is not a part of the programmer-visible API in any useful, observable sense *except to the extent that it can introduce this incompatibility.* From robert.field at oracle.com Mon Nov 29 15:40:00 2010 From: robert.field at oracle.com (Robert Field) Date: Mon, 29 Nov 2010 15:40:00 -0800 Subject: Defender methods and compatibility In-Reply-To: <4CF43202.3040605@univ-mlv.fr> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> <4CF43202.3040605@univ-mlv.fr> Message-ID: <4CF439D0.4090008@oracle.com> On 11/29/10 15:06, R?mi Forax wrote: > On 11/29/2010 11:28 PM, Neal Gafter wrote: >> On Mon, Nov 29, 2010 at 9:53 AM, Reinier Zwitserloot< >> reinier at zwitserloot.com> wrote: >> >>> When is the currently proposed "default method is part of the signature" >>> aspect going to result in a problem? If this is about ideological purity, >>> then I vote we stop painting this bikeshed. >>> >> An interesting reference to Parkinson's *law of triviality*. The basis of >> Parkinson's law is that people seem to care less about issues they don't >> understand, no matter how important those issues might be relative to the >> issues for which they express a preference. "painting this bikeshed" is an >> *ad hominem *suggestion that the participants in this discussion are really >> only commenting on those issues simple enough for them to understand. >> >> In this case, however, you also appear to be saying that you don't fully >> appreciate the issue, and therefore don't see any point in continuing the >> discussion. Which ironically reinforces the reference to Parkinson's law. >> >> To be (hopefully) more clear, here are the costs of the current approach: >> o New syntax for extension methods (as opposed to using the existing method >> body syntax) >> o Changing the default of an extension method is not source compatible (in >> the sense that it can cause some existing clients to no longer compile) >> o Under the current draft spec, not binary compatible either, but there have >> been promised (as yet unexplained) complexities to be added to method >> resolution (and therefore the JVM) that may be introduced to reduce this >> effect. >> >> Here are the benefits: >> o Certain compile-time ambiguities might be avoided when extension methods >> in unrelated classes have the same signature and implementation. > How unrelated classes can have the same default method ? > The spec says that the first parameter of a default method > is the class containing the extension method. I believe this is what is being referred to is a case like this: interface A { public extension int get() default Implementations.one; } interface B { public extension int get() default Implementations.one; } class X implements A, B { } And, Neal, if I understand your point, having this conflict automatically resolve is dangerous, if, for example, A is changed to: interface A { public extension int get() default Implementations.somethingElse; } -Robert >> The practical impact of the source incompatibility is that an API designer >> cannot change an extension method's default without potentially breaking >> some clients (i.e. causing them to no longer compile). That is so because >> the API designer generally does not possess all of the client code of the >> API to test against to ensure they all still compile. Changing the default >> implementation is just the sort of change an API designer would want to make >> from one release to another to, for example, improve performance. But the >> fact that it is an incompatible change inhibits the API designer from making >> such changes in the future. As a consequence, the language feature as >> proposed is more likely to result in future APIs that have more (rather than >> fewer) artifacts of their history. Historical artifacts are one of the pain >> points of the Java platform that we'd be better off minimizing. >> >> The default of an extension method is not a part of the programmer-visible >> API in any useful, observable sense *except to the extent that it can >> introduce this incompatibility.* > R?mi > From alex.buckley at oracle.com Mon Nov 29 16:12:51 2010 From: alex.buckley at oracle.com (Alex Buckley) Date: Mon, 29 Nov 2010 16:12:51 -0800 Subject: Defender methods and compatibility In-Reply-To: <7FDA6630E1822F448C97A48D5D733094028C9558@EXVMSTOR302.intra.rakuten.co.jp> References: <4CEC169B.3040300@oracle.com><4CEC5D4E.4060903@oracle.com><4CEC66C5.3040505@oracle.com><4CED6EF3.5070408@oracle.com><4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com><7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com><4CF32BD8.5050400@oracle.com> <7FDA6630E1822F448C97A48D5D733094028C9558@EXVMSTOR302.intra.rakuten.co.jp> Message-ID: <4CF44183.7050202@oracle.com> Hi Nathan, On 11/29/2010 3:17 PM, Nathan Bryant wrote: > Neal, > > Maybe I just don't get it. In Brian and Oracle's version, changing the > default may be source incompatible, only if the change is to which > static method provides the default. Changing the body of that static > method should usually be source compatible. > > In your preferred solution, there is no source compatibility at all, in > the case where two superinterfaces wish to provide defenders for the > same method. Requiring the implementing class to resolve the ambiguity > is not source compatible. In Neal's scheme, mod-extn is always SC because any ambiguity that could occur in an implementation class was _already_ disambiguated when the second extension method was _added_. (Jim Mayer is quite right in his observation that inline defaults "push some of the risk of breaking source compatibility back on to the act of adding an extension (add-extn).") It's a clever trick, but throwing away the ability to compare default methods is a huge disadvantage given that a single class may obtain defaults from a _graph_ of superinterfaces not just a tree. Alex From forax at univ-mlv.fr Mon Nov 29 16:13:05 2010 From: forax at univ-mlv.fr (=?ISO-8859-1?Q?R=E9mi_Forax?=) Date: Tue, 30 Nov 2010 01:13:05 +0100 Subject: Defender methods and compatibility In-Reply-To: <4CF439D0.4090008@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> <4CF43202.3040605@univ-mlv.fr> <4CF439D0.4090008@oracle.com> Message-ID: <4CF44191.9010406@univ-mlv.fr> On 11/30/2010 12:40 AM, Robert Field wrote: > On 11/29/10 15:06, R?mi Forax wrote: >> On 11/29/2010 11:28 PM, Neal Gafter wrote: >>> On Mon, Nov 29, 2010 at 9:53 AM, Reinier Zwitserloot< >>> reinier at zwitserloot.com> wrote: >>> >>>> When is the currently proposed "default method is part of the signature" >>>> aspect going to result in a problem? If this is about ideological purity, >>>> then I vote we stop painting this bikeshed. >>>> >>> An interesting reference to Parkinson's *law of triviality*. The basis of >>> Parkinson's law is that people seem to care less about issues they don't >>> understand, no matter how important those issues might be relative to the >>> issues for which they express a preference. "painting this bikeshed" is an >>> *ad hominem *suggestion that the participants in this discussion are really >>> only commenting on those issues simple enough for them to understand. >>> >>> In this case, however, you also appear to be saying that you don't fully >>> appreciate the issue, and therefore don't see any point in continuing the >>> discussion. Which ironically reinforces the reference to Parkinson's law. >>> >>> To be (hopefully) more clear, here are the costs of the current approach: >>> o New syntax for extension methods (as opposed to using the existing method >>> body syntax) >>> o Changing the default of an extension method is not source compatible (in >>> the sense that it can cause some existing clients to no longer compile) >>> o Under the current draft spec, not binary compatible either, but there have >>> been promised (as yet unexplained) complexities to be added to method >>> resolution (and therefore the JVM) that may be introduced to reduce this >>> effect. >>> >>> Here are the benefits: >>> o Certain compile-time ambiguities might be avoided when extension methods >>> in unrelated classes have the same signature and implementation. >> How unrelated classes can have the same default method ? >> The spec says that the first parameter of a default method >> is the class containing the extension method. > I believe this is what is being referred to is a case like this: > > interface A { > public extension int get() default Implementations.one; > } > > interface B { > public extension int get() default Implementations.one; > } > > class X implements A, B { } > > And, Neal, if I understand your point, having this conflict > automatically resolve is dangerous, if, for example, A is changed to: > > interface A { > public extension int get() default Implementations.somethingElse; > } > > -Robert But here the default method of A::get is Implementations.one(A) and the default method of B::get is Implementations.one(B). It's not the same method. R?mi From nathan.bryant at linkshare.com Mon Nov 29 16:47:38 2010 From: nathan.bryant at linkshare.com (Nathan Bryant) Date: Tue, 30 Nov 2010 09:47:38 +0900 Subject: Defender methods and compatibility References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> <4CF43202.3040605@univ-mlv.fr><4CF439D0.4090008@oracle.com> <4CF44191.9010406@univ-mlv.fr> Message-ID: <7FDA6630E1822F448C97A48D5D733094028C95B8@EXVMSTOR302.intra.rakuten.co.jp> R?mi wrote: > But here the default method of A::get is Implementations.one(A) and > the default method of B::get is Implementations.one(B). > It's not the same method. > R?mi It could be Implementations.one(Object) I guess. From neal at gafter.com Mon Nov 29 16:51:30 2010 From: neal at gafter.com (Neal Gafter) Date: Mon, 29 Nov 2010 16:51:30 -0800 Subject: Defender methods and compatibility In-Reply-To: <4CF44191.9010406@univ-mlv.fr> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> <4CF43202.3040605@univ-mlv.fr> <4CF439D0.4090008@oracle.com> <4CF44191.9010406@univ-mlv.fr> Message-ID: On Mon, Nov 29, 2010 at 4:13 PM, R?mi Forax wrote: > But here the default method of A::get is Implementations.one(A) and > the default method of B::get is Implementations.one(B). > It's not the same method. > Implementations.one might be declared to take Object as its parameter, in which case this would be the same default. From nathan.bryant at linkshare.com Mon Nov 29 16:54:33 2010 From: nathan.bryant at linkshare.com (Nathan Bryant) Date: Tue, 30 Nov 2010 09:54:33 +0900 Subject: Defender methods and compatibility References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> <4CF43202.3040605@univ-mlv.fr><4CF439D0.4090008@oracle.com> <4CF44191.9010406@univ-mlv.fr> Message-ID: <7FDA6630E1822F448C97A48D5D733094028C95BC@EXVMSTOR302.intra.rakuten.co.jp> R?mi, Perhaps you raise a good point. If the method one() is supposed to be implemented in terms of the existing interface A or B, does it really make sense in many/any cases to erase the type of that interface down to Object. Seems like this can lead to confusing API design with little clear benefit. If A/B want to specify the same default and yet they have no common superinterface, this seems like a strong signal that a new superinterface should be added. Nathan -----Original Message----- From: Nathan Bryant Sent: Monday, November 29, 2010 7:48 PM To: 'R?mi Forax'; lambda-dev at openjdk.java.net Subject: RE: Defender methods and compatibility R?mi wrote: > But here the default method of A::get is Implementations.one(A) and > the default method of B::get is Implementations.one(B). > It's not the same method. > R?mi It could be Implementations.one(Object) I guess. From howard.lovatt at gmail.com Mon Nov 29 17:39:42 2010 From: howard.lovatt at gmail.com (Howard Lovatt) Date: Tue, 30 Nov 2010 12:39:42 +1100 Subject: Defender methods and compatibility Message-ID: I am with Neal on this one, I think the default method should not be part of the method signature. In particular: 1. The case of inheriting the same method twice is rare (see note at end), therefore it is not a large burden to manually resolve this ambiguity and I favour explicit, over implicit, resolution (this is also a characteristic of Java in general). Be consistent with the rest of Java. 2. If the default method becomes part of the signature then it will make including method bodies in interfaces difficult in the future. This is saying that we might want to include method bodies in the future, don't close the door. Be cautious about language change. 3. It will encourage bad practice. Instead of defining a common base interface people will be tempted to just keep adding extension methods. IE it is along the lines of structural typing, whereas Java is primarily nominally typed. Encourage best practice (even though I accept that it is likely to be a rare abuse). 4. Some people think of the default method as part of the implementation detail, others as part of the method signature. To me it is more an implementation detail or more like an annotation. This is clearly a grey area, but my call is 'implementation detail'. -- Howard. Note: The reason I think that inheriting the same method twice is rare is that most cases of multiply inheriting the same method I have seen are variations on the diamond problem, e.g.: interface N { extension void m() default Ns.m; } interface W extends N {} interface E extends N {} class S implements W, E {} and under either set of rules this isn't a problem. The use case that the two rules differ on is: interface W { extension void m() default Ns.m; } interface E { extension void m() default Ns.m; } class S implements W, E {} in the proposed rules this isn't an error but with Neal's rules you need to say: class S implements W, E { public void m() { W.m(); } } // or similar I have seen this later case (in languages that allow multiple inheritance) of defining the same method twice, but I have seen the diamond problem considerable more often and hence think the problem will be rare in practice. From neal at gafter.com Mon Nov 29 17:45:26 2010 From: neal at gafter.com (Neal Gafter) Date: Mon, 29 Nov 2010 17:45:26 -0800 Subject: Defender methods and compatibility In-Reply-To: <4CF44183.7050202@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> <7FDA6630E1822F448C97A48D5D733094028C9558@EXVMSTOR302.intra.rakuten.co.jp> <4CF44183.7050202@oracle.com> Message-ID: On Mon, Nov 29, 2010 at 4:12 PM, Alex Buckley wrote: > In Neal's scheme, mod-extn is always SC because any ambiguity that could > occur in an implementation class was _already_ disambiguated when the > second extension method was _added_. (Jim Mayer is quite right in his > observation that inline defaults "push some of the risk of breaking > source compatibility back on to the act of adding an extension > (add-extn).") > > It's a clever trick, but throwing away the ability to compare default > methods is a huge disadvantage given that a single class may obtain > defaults from a _graph_ of superinterfaces not just a tree. > I don't understand why you say this is a disadvantage. I also don't see why you describe it as a "trick"; I haven't suggested adding anything, only taking away counterproductive complexity in the spec. I suspect all this complexity is attempting to ameliorate a disadvantage of declaration-site extension methods compared to use-site extension methods, which is that some users will want extensions not included in the original interfaces (e.g. java.util collections and Google collections). They will therefore want to develop libraries of extended interfaces with additional extension methods not contemplated in the original set. Clients of these extension interfaces who inherit in a diamond pattern could well experience the kind of ambiguity that you're attempting to support (without ambiguity). However, in the absence of your special treatment of "extension methods with the same default", the designer of the set of extension interfaces (of the authors of the default methods that are widely used as extensions) could arrange to move their new extension method higher in their interface hierarchy, to a new interface if necessary, to achieve the same level of binary and source compatibility for its clients without any special treatment in the language. -Neal From robert.field at oracle.com Mon Nov 29 20:17:27 2010 From: robert.field at oracle.com (Robert Field) Date: Mon, 29 Nov 2010 20:17:27 -0800 Subject: Defender methods and compatibility In-Reply-To: <7FDA6630E1822F448C97A48D5D733094028C95BC@EXVMSTOR302.intra.rakuten.co.jp> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> <4CF43202.3040605@univ-mlv.fr><4CF439D0.4090008@oracle.com> <4CF44191.9010406@univ-mlv.fr> <7FDA6630E1822F448C97A48D5D733094028C95BC@EXVMSTOR302.intra.rakuten.co.jp> Message-ID: <4CF47AD7.2090809@oracle.com> hmmm..., there are a couple of related branch topics (method body location, use-site); Sticking with source compatibility... Assuming a user class X: class X implements A, B { } The interfaces A and B could evolve in the following sequence (where some steps could be combined): 0: initial interface A { } interface B { } 1: add one default: interface A { public int get() default Implementations.one; } interface B { } 2: add the same default to B: interface A { public int get() default Implementations.one; } interface B { public int get() default Implementations.one; } 3: change B's default: interface A { public int get() default Implementations.one; } interface B { public int get() default Implementations.two; } With the current spec, source compatibility breaks (X would have to add a "get" method to disambiguate) at step 3. With Neal's proposal, it would break at step 2. The only "pretty" options would be to have it break at step 1 (which, of course would defeat the whole purpose of having default methods) or to not have it break at all which would mean a (very un-Java-like) ordering on interfaces and the corresponding user-perceived "magic" of when things work. I have to agree with Howard and R?mi/Nathan's arguments that step 2 would be a rare beast. Which leaves little difference twixt the two. -Robert From ali.ebrahimi1781 at gmail.com Mon Nov 29 20:56:47 2010 From: ali.ebrahimi1781 at gmail.com (Ali Ebrahimi) Date: Tue, 30 Nov 2010 08:26:47 +0330 Subject: Defender methods and compatibility In-Reply-To: <4CF47AD7.2090809@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> <4CF43202.3040605@univ-mlv.fr> <4CF439D0.4090008@oracle.com> <4CF44191.9010406@univ-mlv.fr> <7FDA6630E1822F448C97A48D5D733094028C95BC@EXVMSTOR302.intra.rakuten.co.jp> <4CF47AD7.2090809@oracle.com> Message-ID: Hi All, On Tue, Nov 30, 2010 at 7:47 AM, Robert Field wrote: > hmmm..., there are a couple of related branch topics (method body > location, use-site); Sticking with source compatibility... > > Assuming a user class X: > > class X implements A, B { } > > The interfaces A and B could evolve in the following sequence (where > some steps could be combined): > > 0: initial > > interface A { > } > > interface B { > } > > 1: add one default: > > interface A { > public int get() default Implementations.one; > } > > interface B { > } > > 2: add the same default to B: > > interface A { > public int get() default Implementations.one; > } > > interface B { > public int get() default Implementations.one; > } > with current Spec, source compatibility breaks if we have two methods: class Implementations{ public static int one(A a){ /* */} public static int one(B b){ /* */} } otherwise, don' t breaks if: class Implementations{ public static int one(Object a){ /* */} } > > 3: change B's default: > > interface A { > public int get() default Implementations.one; > } > > interface B { > public int get() default Implementations.two; > } > > With the current spec, source compatibility breaks (X would have to add > a "get" method to disambiguate) at step 3. With Neal's proposal, it > would break at step 2. The only "pretty" options would be to have it > break at step 1 (which, of course would defeat the whole purpose of > having default methods) or to not have it break at all which would mean > a (very un-Java-like) ordering on interfaces and the corresponding > user-perceived "magic" of when things work. > > I have to agree with Howard and R?mi/Nathan's arguments that step 2 > would be a rare beast. Which leaves little difference twixt the two. > > -Robert > > > Ali Ebrahimi From peter.levart at marand.si Tue Nov 30 00:18:35 2010 From: peter.levart at marand.si (Peter Levart) Date: Tue, 30 Nov 2010 09:18:35 +0100 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CF44183.7050202@oracle.com> Message-ID: <201011300918.35812.peter.levart@marand.si> On 11/30/10, Neal Gafter wrote: > On Mon, Nov 29, 2010 at 4:12 PM, Alex Buckley wrote: > > > In Neal's scheme, mod-extn is always SC because any ambiguity that could > > occur in an implementation class was _already_ disambiguated when the > > second extension method was _added_. (Jim Mayer is quite right in his > > observation that inline defaults "push some of the risk of breaking > > source compatibility back on to the act of adding an extension > > (add-extn).") > > > > It's a clever trick, but throwing away the ability to compare default > > methods is a huge disadvantage given that a single class may obtain > > defaults from a _graph_ of superinterfaces not just a tree. > > > > I don't understand why you say this is a disadvantage. I also don't see why > you describe it as a "trick"; I haven't suggested adding anything, only > taking away counterproductive complexity in the spec. > > I suspect all this complexity is attempting to ameliorate a disadvantage of > declaration-site extension methods compared to use-site extension methods, > which is that some users will want extensions not included in the original > interfaces (e.g. java.util collections and Google collections). They will > therefore want to develop libraries of extended interfaces with additional > extension methods not contemplated in the original set. Clients of these > extension interfaces who inherit in a diamond pattern could well experience > the kind of ambiguity that you're attempting to support (without ambiguity). I'm particularly concerned about this scenario. Lat's say vendor A (Oracle) defines unrelated interfaces A1 and A2 with a shared extension method because it assumes they might be used in a diamond pattern in implementing classes. Vendor B (Google) sees this practice and defines unrelated interface B1 with the same extension method pointing to same default because it assumes their interface might be used combined with interfaces A1 and/or A2 in implementing classes. Now vendor A has locked itself out of changing the "signature" of extension method in interfaces A1 and A2 since changing it would brake SC (and BC) in those classes that implement A1/A2 and B1. > > However, in the absence of your special treatment of "extension methods with > the same default", the designer of the set of extension interfaces (of the > authors of the default methods that are widely used as extensions) could > arrange to move their new extension method higher in their interface > hierarchy, to a new interface if necessary, to achieve the same level of > binary and source compatibility for its clients without any special > treatment in the language. > > -Neal > So the preferred way of defining a common extension method which could be re-used by other vendors is to define a common super-interface with a single extension method which all concerned interfaces would extend. No SC breakage would result if this "single" definition is ever changed (being inline or not). Peter From mthornton at optrak.co.uk Tue Nov 30 01:18:36 2010 From: mthornton at optrak.co.uk (Mark Thornton) Date: Tue, 30 Nov 2010 09:18:36 +0000 Subject: Defender methods and compatibility In-Reply-To: <201011300918.35812.peter.levart@marand.si> References: <4CEC169B.3040300@oracle.com> <4CF44183.7050202@oracle.com> <201011300918.35812.peter.levart@marand.si> Message-ID: <4CF4C16C.5020802@optrak.co.uk> On 30/11/2010 08:18, Peter Levart wrote: > So the preferred way of defining a common extension method which could > be re-used by other vendors is to define a common super-interface with > a single extension method which all concerned interfaces would extend. > No SC breakage would result if this "single" definition is ever > changed (being inline or not). That common super interface needs to include all the methods relevant to the extension method. Mark From reinier at zwitserloot.com Tue Nov 30 02:48:41 2010 From: reinier at zwitserloot.com (Reinier Zwitserloot) Date: Tue, 30 Nov 2010 11:48:41 +0100 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> Message-ID: On Mon, Nov 29, 2010 at 11:28 PM, Neal Gafter wrote: To be (hopefully) more clear, here are the costs of the current approach: > o New syntax for extension methods (as opposed to using the existing method > body syntax) > Not related. If at some point the ability to specify the default directly as a method body in the interface is added, there's still a default of some sort. Possibly this will point at itself (InterfaceName.methodWithInlineDefault), possibly the method body is desugared into some form of method, and the 'default method' becomes this generated construct. Either way, there's still the concept of realizing 2 different defaults happen to point at the same method. In other words, this is NOT a cost of the "current approach" (being: default method is relevant for Source Compatibility). If the "default MethodRef" syntax goes away entirely, this point becomes moot, as all defaults will always point at incompatible things. > o Changing the default of an extension method is not source compatible (in > the sense that it can cause some existing clients to no longer compile) > This is a trivial detail that's not worth holding back progress for, for two reasons. (1) Such a change leading to an actual compile error for existing code is exceedingly rare. The same level of rare as adding new methods to existing classes in rt.jar, which could cause problems in code extending the class and also having added this method. This is in fact _why_ I asked you for realistic use cases. If I wanted some arrogant guy insulting me, I'd have asked for that instead. (2) In the few cases where the maintainer of the interface realizes this could become a problem, he can change the default implementation instead. That would be an entirely source (and binary) compatible change. This is not of course possible if this maintainer does not control the default method, but I again ask for practical situations where that would be the case. For example, List will possibly get a sort() method, defaulted to Collections.sort. Both List and Collections are in the same "module" - whomever has the ability to change the one has the ability to change the other. If, on the other hand, you could make a case that regardless of these 2 reasons, there's still a decent chance that Source Incompatible changes are going to be likely, I'm all ears. But, to channel Brian for a moment, please show some examples instead of hammering on about "should", "broken", and other meaningless fearmongering. o Under the current draft spec, not binary compatible either, but there have > been promised (as yet unexplained) complexities to be added to method > resolution (and therefore the JVM) that may be introduced to reduce this > effect. > > Not relevant either: Reason 1: It's easy to imagine a fix. There's a mechanism for the classloader or VM to fix a class implementing interface X and missing the default method to link it to the default. There's also the option for the compiler to do so at compile time. If the runtime mechanism exists anyway, we have a path of fixing a change in default method. Reason 2: See previous section's Reason 2. Reason 3: Unless you're talking about doing away with the "default X" mechanism entirely and going with "supply body inline", how do you propose to fix this? You're doing the same thing: You insinuate a way to get rid of the complexities without explaining how. Here are the benefits: > o Certain compile-time ambiguities might be avoided when extension methods > in unrelated classes have the same signature and implementation. > Surely you mean unrelated /interfaces/, not classes. That's a rather creative definition of "unrelated". If the default implementation is the same, odds are the 2 interfaces are related (such as: are from the same "module", i.e. List and Map). The default of an extension method is not a part of the programmer-visible > API in any useful, observable sense *except to the extent that it can > introduce this incompatibility.* > > Vs. not introducing it, in which case the potential incompatibility is turned into a sure thing. How is that practical? Or are you suggesting that there's an arbitrary but deterministic algorithm for picking the winner, such as "whichever interface is listed first"? From maurizio.cimadamore at oracle.com Tue Nov 30 08:19:05 2010 From: maurizio.cimadamore at oracle.com (Maurizio Cimadamore) Date: Tue, 30 Nov 2010 16:19:05 +0000 Subject: Defender methods and compatibility In-Reply-To: References: Message-ID: <4CF523F9.5000300@oracle.com> On 30/11/10 01:39, Howard Lovatt wrote: > I am with Neal on this one, I think the default method should not be > part of the method signature. In particular: > > 1. The case of inheriting the same method twice is rare (see note at > end), therefore it is not a large burden to manually resolve this > ambiguity and I favour explicit, over implicit, resolution (this is > also a characteristic of Java in general). Be consistent with the rest > of Java. > Hi About being consistent with the rest of Java, I'd like to point out a kind of failure that is reminescent of what has been discussed in this (and related) thread; suppose we have the following class hierarchy: class A { void m(int i, Object o) {} } class B extends A { } And a client class: class Client { { new B().m(1,1); } } Now, suppose to add a method to B (which is a binary compatible operation): class B extends A { void m(Object o, int i) {} } The client will now start to fail, because the call-site has become ambiguous (resolution has now two unrelated version of m to choose from). To some extent, implicit resolution have been supported in Java for a long time (overload resolution); sometimes like in the case above, you might think that having implicit resolution is bad, as the outcome of the compilation process is kinda unexpected. However, I think that an implicit resolution strategy must be evaluated in terms of 'how well does it perform in the general case' and not on the basis on obscure corner cases (as the one above is). Maurizio From neal at gafter.com Tue Nov 30 09:56:48 2010 From: neal at gafter.com (Neal Gafter) Date: Tue, 30 Nov 2010 09:56:48 -0800 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> Message-ID: On Tue, Nov 30, 2010 at 2:48 AM, Reinier Zwitserloot < reinier at zwitserloot.com> wrote: > On Mon, Nov 29, 2010 at 11:28 PM, Neal Gafter wrote: > > To be (hopefully) more clear, here are the costs of the current approach: >> o New syntax for extension methods (as opposed to using the existing >> method body syntax) >> > > Not related. If at some point the ability to specify the default directly > as a method body in the interface is added, there's still a default of some > sort. > It is related in the sense that the new syntactic footprint adds nothing else than the ability to take the identity of the default into account in the semantics. Without doing that, there is no point in adding the new syntactic form to the language. I consider it a disadvantage to add to the language's syntactic footprint without any purpose. > > o Changing the default of an extension method is not source compatible (in >> the sense that it can cause some existing clients to no longer compile) >> > > This is a trivial detail that's not worth holding back progress for > Was somebody proposing to hold back progress? This is actually a critical detail, the resolution of which will advance the purpose of this language change. , for two reasons. (1) Such a change leading to an actual compile error for > existing code is exceedingly rare. > Either that or perhaps it is exceedingly common. We don't have enough experience with the feature to really know. The nature of the incompatibility is unlike any existing incompatibility, among other reasons that overload resolution is used to find the matching method. (2) In the few cases where the maintainer of the interface realizes this > could become a problem, he can change the default implementation instead. > There are three problems with this. First, the maintainer is likely not to realize there is a problem. Changing an extension method's default is nearly *always* source incompatible in the sense that there can exist clients that would break. Second, it might not be possible to change the implementation of the default's body because the programmer doesn't control the sources of the default's body. See my next paragraph for a simple example of this. Third, the new default may need to have a more specific receiver type, which is very commonly the case when improving the performance of an API. That would be an entirely source (and binary) compatible change. This is not > of course possible if this maintainer does not control the default method, > but I again ask for practical situations where that would be the case. For > example, List will possibly get a sort() method, defaulted to > Collections.sort. Both List and Collections are in the same "module" - > whomever has the ability to change the one has the ability to change the > other. > The simplest example would be Google releasing extended collection interfaces that use as defaults static methods that are part of the JDK. Or consider a more elaborate example: First, Google provides the third-generation "collections" APIs with static methods for operations that Oracle did not include as extension methods. Then, someone (say, you) defines extension interfaces that use these as defaults. Later, Google improves the implementation of these methods by providing strictly more specific, behaviorally compatible static overloads. That is normally a source and binary compatible change. However, because the defaults are specified in your sources by name only, when you recompile you get the new, improved implementation as your default in only those interfaces that are subtypes of the new first argument (receiver) type. This can cause your sources to compile just fine if the conflict doesn't arise in your source. But it can break the source code of third parties that implement your extension interfaces. Who do we blame for the problem? Clients of your interfaces would blame you, but you'd rightfully point out that there was no potential issue when you first created your interfaces. You might blame Google for adding overloads to existing methods, but Google would rightfully point out that the new overloads are strictly more specific than the old ones and behaviorally compatible (only more efficient). You might blame clients of your interfaces for inheriting in a diamond pattern, but the ability to do so is one of the principal benefits of interfaces over classes. The root of the problem is the language specification that enables such incompatibilities to arise so late in the game for seemingly innocuous reasons. It would have been better for the defaults method bodies to appear inline, so that any client who inherits in a diamond pattern will be forced to explicitly disambiguate potentially conflicting default methods on the very first compile. o Under the current draft spec, not binary compatible either, but there have >> been promised (as yet unexplained) complexities to be added to method >> resolution (and therefore the JVM) that may be introduced to reduce this >> effect. >> >> > Not relevant either: > It is relevant, because keeping the platform simpler is better, all other things being equal. > Reason 3: Unless you're talking about doing away with the "default X" > mechanism entirely and going with "supply body inline", how do you propose > to fix this? You're doing the same thing: You insinuate a way to get rid of > the complexities without explaining how. > I would prefer to see the body inline, but it could also be fixed by removing reference to the identity of the default in the specification. > Here are the benefits: >> o Certain compile-time ambiguities might be avoided when extension methods >> in unrelated classes have the same signature and implementation. >> > > Surely you mean unrelated /interfaces/, not classes. That's a rather > creative definition of "unrelated". If the default implementation is the > same, odds are the 2 interfaces are related (such as: are from the same > "module", i.e. List and Map). > I'm not suggesting we design the language based on the odds, but based on sound software engineering and language design principles. In any case, I believe you're wrong, as my example above suggests. > The default of an extension method is not a part of the programmer-visible >> API in any useful, observable sense *except to the extent that it can >> introduce this incompatibility.* >> >> > Vs. not introducing it, in which case the potential incompatibility is > turned into a sure thing. > Actually, without this special treatment there is never be an ambiguity created by changing an extension method's implementation. From brian.goetz at oracle.com Tue Nov 30 11:35:35 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 30 Nov 2010 14:35:35 -0500 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> Message-ID: <4CF55207.4030008@oracle.com> >> This is a trivial detail that's not worth holding back progress for > > Was somebody proposing to hold back progress? The sad reality is that a fair portion of the postings to lambda-dev, no matter how well-intentioned, have a negative effect on progress. (The exceptions are mostly the bug reports (thanks Thomas, keep 'em coming!)) It takes time to read, consider, and respond. And messages nearly always arrive at a time when you are working on something else, causing context switch overhead. The question is whether the potential incremental benefit that comes from the discussion is worth the incremental cost in time and schedule risk. Unfortunately nearly none of the contributors in this forum have all the facts (e.g., schedule, budget, and other non-public constraints) with which to make that tradeoff. But every discussion has a real cost to the community in the form of incremental schedule risk. I can tell you for a fact that the discussions on this list have frequently contributed to the schedule risk for delivering lambda. So please, use some judgment. It may be fun to argue language design, but excessive argument here hurts the entire Java community. This specific discussion does not show signs of convergence, and as such has such an an impediment to progress. At this point I see only downside in continuing the argument. What has been said here may well be considered by the EG when the appropriate time comes. From pbenedict at apache.org Tue Nov 30 12:04:36 2010 From: pbenedict at apache.org (Paul Benedict) Date: Tue, 30 Nov 2010 14:04:36 -0600 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> Message-ID: Too bad the JDK can't be configured at run-time to resolve the default method implementations according to how *I* want it. Must it be specified in the class file? Perhaps it could be done as an SPI or META-INF external configuration. That way, you let application designers determine their defaults for their own programs (and not the original interface writer). Quite frankly, I sometimes think this extension/default thing is a very weak (and awkward) mix-in implementation. If I could control what implementation to use, that beats any solution provided so far. The JDK could come with a default mix-in implementation, but let me determine how to customize it. From pbenedict at apache.org Tue Nov 30 12:04:36 2010 From: pbenedict at apache.org (Paul Benedict) Date: Tue, 30 Nov 2010 14:04:36 -0600 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> Message-ID: Too bad the JDK can't be configured at run-time to resolve the default method implementations according to how *I* want it. Must it be specified in the class file? Perhaps it could be done as an SPI or META-INF external configuration. That way, you let application designers determine their defaults for their own programs (and not the original interface writer). Quite frankly, I sometimes think this extension/default thing is a very weak (and awkward) mix-in implementation. If I could control what implementation to use, that beats any solution provided so far. The JDK could come with a default mix-in implementation, but let me determine how to customize it. From alex.buckley at oracle.com Tue Nov 30 12:32:10 2010 From: alex.buckley at oracle.com (Alex Buckley) Date: Tue, 30 Nov 2010 12:32:10 -0800 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> Message-ID: <4CF55F4A.8080700@oracle.com> On 11/30/2010 9:56 AM, Neal Gafter wrote: > Or consider a more elaborate example: First, Google provides the > third-generation "collections" APIs with static methods for operations that > Oracle did not include as extension methods. > > Then, someone (say, you) defines extension interfaces that use these as > defaults. > > Later, Google improves the implementation of these methods by providing > strictly more specific, behaviorally compatible static overloads. That is > normally a source and binary compatible change. However, because the > defaults are specified in your sources by name only, when you recompile you > get the new, improved implementation as your default in only those > interfaces that are subtypes of the new first argument (receiver) type. > This can cause your sources to compile just fine if the conflict doesn't > arise in your source. But it can break the source code of third parties > that implement your extension interfaces. Yes; this echoes Ali's point, where overloading of static methods specified as defaults can break SC for the interface specifying the defaults. A partial solution is for an interface method with a default to denote the full signature of the default. An interface owner who moves from an older Google method to a newer, more specific (in the first arg) Google method is then clearly doing a mod-extn, and may have to answer to annoyed implementers. > Who do we blame for the problem? ... You might blame Google for adding > overloads to existing methods, but Google would rightfully point out that > the new overloads are strictly more specific than the old ones and > behaviorally compatible (only more efficient). Nevertheless, adding a method has never been guaranteed to be source compatible. Alex From neal at gafter.com Tue Nov 30 13:09:57 2010 From: neal at gafter.com (Neal Gafter) Date: Tue, 30 Nov 2010 13:09:57 -0800 Subject: Defender methods and compatibility In-Reply-To: <4CF55207.4030008@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> <4CF55207.4030008@oracle.com> Message-ID: On Tue, Nov 30, 2010 at 11:35 AM, Brian Goetz wrote: > This is a trivial detail that's not worth holding back progress for >>> >> >> Was somebody proposing to hold back progress? >> > > The sad reality is that a fair portion of the postings to lambda-dev, no > matter how well-intentioned, have a negative effect on progress. If by "progress" you mean completion and delivery of language changes (no matter how ill-advised in their design) I can easily believe that. While I do believe that Oracle would have less schedule risk in the absence of the discussions on this list, I also believe that these discussions have the potential to improve the language design (in the sense of reducing risks to future users of the language that arise out of poor choices in language design) in a way that more than offsets (from the perspective of the users of the language) the schedule risk. These risks - risks to the future users of the language - might well be valued differently by members of this list than by Oracle. Of course these risks do need to be balanced against each other, as spending infinite resources on design ensures that users never see any language change. Oracle does not have a well established history of collaborative engineering in this kind of forum, where such risks can be balanced by the participants, but we can hope that this forum provides an opportunity for change. The question is whether the potential incremental benefit that comes from > the discussion is worth the incremental cost in time and schedule risk. > Unfortunately nearly none of the contributors in this forum have all the > facts (e.g., schedule, budget, and other non-public constraints) with which > to make that tradeoff. That is why this mailing list is so valuable: it enables the participants to share, discuss, and balance risks of all kinds, including risks to future users of the language that Oracle might not fully appreciate (or care about as much as its users). From brian.goetz at oracle.com Tue Nov 30 13:32:37 2010 From: brian.goetz at oracle.com (Brian Goetz) Date: Tue, 30 Nov 2010 16:32:37 -0500 Subject: Defender methods and compatibility In-Reply-To: References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> <4CF55207.4030008@oracle.com> Message-ID: <4CF56D75.4070401@oracle.com> > While I do > believe that Oracle would have less schedule risk in the absence of the > discussions on this list, I also believe that these discussions have the > potential to improve the language design These discussions certainly do have the *potential* to improve the language design (which is why we're doing this at all -- its certainly not the most resource-efficient approach!). And in many cases, they have, and we appreciate that. But they also have the potential for evil as well as good. In some cases, they have simply been a huge distraction and a waste of time for no benefit, such as last week's discussion over mutable local variables, when the issue had already been extensively explored, considered, and rejected for the current round, and the discussion amounted to "I would like it better if." I would say the entire community was a loser for that; it distracted us for nearly a week. That's a week closer to not delivering anything into Java 8. Our runway is far from infinite and there's a lot of work to do. > Of course these risks do need to be balanced against each other, as spending > infinite resources on design ensures that users never see any language > change. Oracle does not have a well established history of collaborative > engineering in this kind of forum, where such risks can be balanced by the > participants, but we can hope that this forum provides an opportunity for change. This collaborative approach is an experiment on our part towards greater community engagement. If the community cannot meet us halfway by showing some restraint (for example, by recognizing when it has ratholed and backing off), it will be a failed experiment, for which we will all be losers. From jim at pentastich.org Tue Nov 30 19:34:29 2010 From: jim at pentastich.org (Jim Mayer) Date: Tue, 30 Nov 2010 22:34:29 -0500 Subject: Defender methods and compatibility In-Reply-To: <4CF56D75.4070401@oracle.com> References: <4CEC169B.3040300@oracle.com> <4CEC5D4E.4060903@oracle.com> <4CEC66C5.3040505@oracle.com> <4CED6EF3.5070408@oracle.com> <4CED9FF5.2080308@oracle.com> <4CEDA437.1070904@oracle.com> <7A23D52F-D635-4D09-822D-C9144332FE8E@oracle.com> <4CF32BD8.5050400@oracle.com> <4CF55207.4030008@oracle.com> <4CF56D75.4070401@oracle.com> Message-ID: > > > This collaborative approach is an experiment on our part towards greater > community engagement. If the community cannot meet us halfway by showing > some > restraint (for example, by recognizing when it has ratholed and backing > off), > it will be a failed experiment, for which we will all be losers. > > Brian, When you called for a close on the mutable local variables discussion most people stopped (and what continued was mostly sparked by folks catching up with list traffic). I, for one, would have preferred a different outcome. On the other hand, I agreed with your analysis that: (1) The feature was controversial. (2) The choice being made was conservative, as the current restrictions could be relaxed in the future without breaking backwards compatibility. I also accepted the premise that you set out at the beginning of that discussion when you said: > In order to justify the complexity that these new features would generate, there needs to be a compelling use case. So, what is the compelling use case for adding a special disambiguation rule? I haven't seen one. In fact, the closest I've seen to a concrete use case was Neal's Google "third generation collections API" horror story. It also seems to me that the rule could be added later without breaking backwards compatibility. Anyway, that's my two cents. -- Jim P.S., In the spirit of "chickens" everywhere, here's a concrete proposal: (1) Drop the "same initializer" disambiguation rule. (2) Change the syntax to follow the short hand for method references (E.g., "extension void m() default #SomeClass.method;") By making the syntax be compatible with the short hand for method references we open up the possibility of using lambda expressions to specify the default. This is cool on several fronts: (a) It looks like a constant definition (one could even play with swapping 'default' for '='). They're already legal in interfaces. (b) It could be extended in the future to actually BE a constant definition (E.g., "#{thing->...}", "SomeFactory.THE_DEFAULT" or even "SomeFactory.theDefaultOperation()"). This would provide for inline specification if it proved useful while remaining true to the historical "feel" of Java interfaces. (c) It could be left as a restricted form and the special disambiguation rule could be reintroduced. Just a thought :-)