tag:blogger.com,1999:blog-741750605858169835.post4922678221551918346..comments2024-01-24T14:53:02.919+00:00Comments on Stephen Colebourne's blog: First-Class Methods: Java-style closuresStephen Colebournehttp://www.blogger.com/profile/01454237967846880639noreply@blogger.comBlogger38125tag:blogger.com,1999:blog-741750605858169835.post-27993274083316877372007-03-06T02:28:14.000+00:002007-03-06T02:28:14.000+00:00A while back I proposed another alternative, Clear...A while back I proposed another alternative, Clear, Consistent, and Concise Syntax (C3S) for Java (http://www.artima.com/weblogs/viewpost.jsp?thread=182412). It is similar to yours in that it is based around inner classes, but has short syntax. Another difference is that it is an error if you don't supply all the methods in an interface/abstract class. Your example in my proposal is:<br /><br />List< String > list = Collections.sort list, method( String str1, String str2 ) {<br />__str1.length() - str2.length()<br />};Howard Lovatthttp://www.artima.com/weblogs/viewpost.jsp?thread=182412noreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-82901098874188982402007-03-05T17:35:06.000+00:002007-03-05T17:35:06.000+00:00@Stephan, Point 1: You are correct that the # is u...@Stephan, Point 1: You are correct that the # is unnecessary to the compiler. However, we argue that it *is* necessary for the *human* reading the code at a later date.<br /><br />Point 2: I'm sure that there are many alternatives to #. I started off with -> if you look back through my blog. Now I've used it, I quite like it, but its an aesthetic thing and depends on what other languages you've used.<br /><br />Point 3: Again, you are correct that the compiler could infer the types. However, that is unlike any other location in Java, and greatly increases the complexity of the proposal. If such type inference is to occur it should be a separate proposal which affects all of Java, such as using a new 'var' keyword.Stephen Colebournenoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-61005435213007751582007-03-05T13:42:46.000+00:002007-03-05T13:42:46.000+00:00Stephen, I have several comments about your propos...Stephen, I have several comments about your proposal.<br /><br />First, I think # is unnecessary when declaring method:<br /><br />List list = ...<br />Collections.sort(list, (String str1, String str2) {<br />__return str1.length() - str2.length();<br />});<br /><br />This syntax is similar to <a href="http://www.javac.info/closures-v02.html" rel="nofollow">BGGA v0.2</a>, that is perfect at my opinion (and as in v0.2 I would like to remove function types).<br /><br />Second, I'd prefer to change hash # symbol to :: . # just points to HTML anchor.<br /><br />And third, most important: in the most cases, argument types can be omitted:<br /><br />List list = ...<br />Collections.sort(list, (str1, str2) {<br />__return str1.length() - str2.length();<br />});<br /><br />Collection.sort accepts (List, Comparator), so if first argument is List, then second argument must be Comparator, so declaring argument types is not necessary.Stepan Koltsovnoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-79232586175481980792007-03-01T13:23:44.000+00:002007-03-01T13:23:44.000+00:00@Matthew, What is the use case for needing yield a...@Matthew, What is the use case for needing yield and return? If you read http://gafter.blogspot.com/2006/08/use-cases-for-closures.html then you'll find we are tackling only the asynchronous use cases with this proposal (hopefully, we'll produce a separate document to discuss the synchronous use case). I don't believe that the asnyc use case needs both a yield and a return.<br /><br />@Michael, Maybe I've missed something, but I think you'll find the proposal covers all your requirements.Stephen Colebournenoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-62776151221470144292007-03-01T02:47:13.000+00:002007-03-01T02:47:13.000+00:00I'm not familiar with the internals of the JVM...I'm not familiar with the internals of the JVM, but would it be possible to overload functions on a per-reference basis? I know scripting languages inside the JVM can dynamically redefine methods and add interface implementation to an instance, so maybe it's possible.<br /><br />I was thinking something that would work like this:<br /><br />Collections.sort(list, <br /> new Comparator.compare(Object a, Object b) {<br /> //.. compare code<br /> }<br />);<br /><br />Which would create a new anonymous class that extends Object and implements Comparator, with the compare method defined in the block. This is really just a simplified inner class, but what about something like this:<br /><br />JButton button = new JButton("Hello World");<br />button.addActionListener(<br /> this$ActionListener.actionPerformed(ActionEvent e) {<br /> //.. Action code<br /> }<br />);<br /><br />This would return a reference to 'this' that is overloaded such that calling 'actionPerformed' on that reference would execute the code block, while calling 'actionPerformed' on another reference to the same object may result in executing a different code block.<br /><br />Is something like this even feasible without major modifications to the JVM and/or bytecode spec?Michaelnoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-82716490180447181872007-03-01T01:11:39.000+00:002007-03-01T01:11:39.000+00:00I don't see why the same argument for redefini...I don't see why the same argument for redefining "this" in an inner method couldn't apply to my argument for redefining "return." In a block they could *both* be in terms of the enclosing scope.<br /><br />The problem is that if you don't plan for *some* way to return from the enclosing scope before implementation, it will be too late to fix it later.Matthew Hallhttp://paperclips.sourceforge.net/noreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-82176659242180189832007-03-01T00:08:51.000+00:002007-03-01T00:08:51.000+00:00@Michael, Collections.sort currently takes a Compa...@Michael, Collections.sort currently takes a Comparator as its second parameter, and FCM is simply adapting to that Comparator. An inner method differs from an inner class in the semantics of 'this'. In an inner class 'this' has complex meaning, in an inner method it just refers to the class that surrounds it in source code.<br /><br />@Matthew, Using 'return' is simply more method-like, and familiar to exiting developers. For the cases that we are tackling, there is no need for returning from the surrounding method.Stephen Colebournenoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-17875653976579500122007-02-28T21:20:57.000+00:002007-02-28T21:20:57.000+00:00This is slightly off-topic, but as far as the abil...This is slightly off-topic, but as far as the ability to distinguish between returning from the enclosing method vs returning from the inner method, why not just introduce the "yield" operator to return from the inner method, and have "return" return from the enclosing method? Just a thought.Matthew Hallhttp://paperclips.sourceforge.net/noreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-31222320195295273062007-02-28T19:38:56.000+00:002007-02-28T19:38:56.000+00:00My main concern with all these closure proposals i...My main concern with all these closure proposals is that even if I don't like them, libraries will start popping up that require I use them. Additionally, for the people who do like them, existing libraries will need to be modified to allow their use, am I right? Collections.sort doesn't currently accept arguments in the form or Method or Method Reference. <br /><br />I see that your proposal can assign your Method reference to an Interface method, and return an object implementing that interface, which would solve part of the problem. But how is that any different than an anonymous inner class definition? Just shorter?Michael Hallnoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-64141724040502226352007-02-28T18:41:58.000+00:002007-02-28T18:41:58.000+00:00Well, I lost this part on Stephen ;)Well, I lost this part on Stephen ;)Stefan Schulzhttp://jroller.com/page/jaddanoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-62180116886242596122007-02-28T18:20:58.000+00:002007-02-28T18:20:58.000+00:00Object#toString().invoke(obj)
And you'd have ...Object#toString().invoke(obj)<br /><br />And you'd have to catch all the associated exceptions, which is odd because this can be statically checked.Ricky Clarksonhttp://cime.net/~ricky/noreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-17925306846508995302007-02-28T18:03:02.000+00:002007-02-28T18:03:02.000+00:00@Carlos: Why would you prefer such a difficult to ...@Carlos: Why would you prefer such a difficult to read syntax over creating and inner class?<br /><br />@Ricky: Thanks, Ricky. :)<br />Btw. the reason that Object#toString() is not invocable is the same as that Object.toString() is not invocable (in contrast to obj#toString()). Of course, you could automatically let the compiler wrap it into <br />#(Object obj) { return obj.toString(); }<br />But that's not the same. On the other hand, you can invoke it like follows:<br />Object#toString().invoke(obj);<br />as the first becomes an instance of Method.Stefan Schulzhttp://jroller.com/page/jaddanoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-52294990658800451272007-02-28T17:33:23.000+00:002007-02-28T17:33:23.000+00:00I suppose between all these alternatives something...I suppose between all these alternatives something will emerge that will:<br /><br />a) Facilitate familiarity for newcomers from languages like C/C++. (Perl users will be right at home of course)<br /><br />b) Keep installed Java base put.<br /><br />So many people working on it, its bound to work out.jnicehttp://niceforj.comnoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-78620316533272309842007-02-28T16:47:10.000+00:002007-02-28T16:47:10.000+00:00Although I've already mentioned most of this i...Although I've already mentioned most of this in email to Stephen and Stefan, I'd just like to point out what I consider to be right and wrong with this proposal.<br /><br />Method references appear to be done absolutely right, as do inner methods, except that a reference to an instance method is not invocable. The use of # looked odd to me at first, but it's clear that '.' would be hard to read for some cases, and other syntaxes would look less like existing Java. If the proposal was about runnable blocks, like the BGGA is, then the syntax being method-like would be a disadvantage. As it is method-orientated, it is an advantage.<br /><br />In discussions with Ste{phe,fa}n, I never seemed to get a conclusive reason why Object#toString() couldn't be invoked. To me, it seems that if you pass an object to it as the first parameter, you could invoke it, that is: Object#toString() is a #(String(Object)) (a method that takes an Object and delivers a String). The proposal does, however, let you do instance#toString(), and that is invocable.<br /><br />I admire the way they both completely ignored my suggestions about introducing inference, that probably keeps the spec readable and simple. ;)<br /><br />The limitations of the generic type system mean that this proposal, and the BGGA, need to generate separate interfaces for each type of method/function. One big reason is that generics don't do varargs, so you can't write interface Function<br />{<br /> Return invoke(Params params...) throws Exceptions;<br />}<br /><br />Of course, there is some ambiguity in the above, you'd need to say where params stop and exceptions start, but if the generic type system included varargs, plus some way of delimiting sets of type parameters, I think we'd be onto a winner. Neal thinks, for the case I presented to him, it would be simpler just to add a currying/partial function evaluation operator, but the fact that you can't do this as a language user rather than designer shows the limits of the type system.Ricky Clarksonhttp://cime.net/~ricky/noreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-40539517255160278912007-02-28T13:07:12.000+00:002007-02-28T13:07:12.000+00:00What about using a array-like syntax for implement...What about using a array-like syntax for implementing multiple methods? Something like:<br /><br />MouseListener lnr = { <br /> #mouseClicked(MouseEvent ev) { ... },<br /> this#mouseReleased(MouseEvent ev),<br /> thid#mousePressed(MouseEvent ev),<br /> #mouseEntered(MouseEvent ev) { ... },<br /> #mouseExited(MouseEvent ev) { ... }<br />};Carlos Gessernoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-11057229494716947812007-02-28T11:18:24.000+00:002007-02-28T11:18:24.000+00:00@Matthew, Your example is a good one. One possible...@Matthew, Your example is a good one. One possible solution would be to only allow an override of a class/interface with multiple abstract methods if all the methods not overridden return void. That would handle the MouseListener case, and block the List example.Stephen Colebournenoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-3800902953401633762007-02-28T07:13:02.000+00:002007-02-28T07:13:02.000+00:00Matthew, your explanation is sound and I somewhat ...Matthew, your explanation is sound and I somewhat agree. At least it should be considered in the next version of the proposal either in form of allowing assignments to SAM-providing and non-abstract classes only, or by adding a rationale about a converse decision.Stefan Schulzhttp://jroller.com/page/jaddanoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-79939176029953775312007-02-28T06:51:23.000+00:002007-02-28T06:51:23.000+00:00My only real beef is where you say that nullary me...My only real beef is where you say that nullary methods would be automatically created when there are multiple abstract methods (wide interfaces). Outside the domain of event listeners this approach is completely wrong. Case in point:<br /><br />List numberedElements = #get(int index) {<br /> return "Element #"+index;<br />}<br /><br />As soon as we call a method other than get(int) we're going to get nonsense answers. That's not the intended use for the syntax but it's a real possibility. This is why I feel that the case for implementing multiple abstract methods with the # syntax should be disallowed. There can only be a single abstract method, period.<br /><br />Therefore if we want a concise event listener on a wide interface, we have to start from a base implementation which already implements all the other methods, hence my first proposal. It's the only way (famous last words) that I can see to be sure that interfaces are being implemented correctly.<br /><br />Other than this one small item, I very much support your proposal, and hope it gets the attention and traction it deserves.Matthew Hallhttp://paperclips.sourceforge.net/noreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-83387417606076207712007-02-28T01:57:12.000+00:002007-02-28T01:57:12.000+00:00@Matthew, As Stefan said, we had the MouseAdapter#...@Matthew, As Stefan said, we had the MouseAdapter#mouseClicked(MouseEvent ev) syntax of yours in the proposal for a while. But the problem is that it places the emphasis on the class, whereas we want the emphasis to be on the method. Put simply, if you want to create a class, with constructor arguments and allowing this in the method to reference the class, then write an inner class! (Inner classes aren't 'old syntax' with FCM). An inner method has a different meaning.<br /><br />I agree that converting an inner class to an inner method isn't necessary free because of the different 'this' semantics. But the semantic change is essential.<br /><br />When you write an inner class, it is clear and visible in the source code that a class is being created, so it is reasonable for 'this' to bind to that inner class. When you write an inner method, there is no visible surrounding class other than that which contains the method holding the inner method. So 'this' should naturally refer to the surrounding object, not a construct that doesn't appear in the source code.<br /><br />@Quintesse, I'll try and compare the three proposals in a new blog soon.<br /><br />@Alex, We exclude the control-invocation syntax, so there is no need for special loop constructs, non-local returns, break/continue as exceptions. Thats why this proposal is a lot simpler to implement and get right than BGGA.<br /><br />@jnice, A number of your points refer to a dislike of #. This character was chosen as (a) its already used in javadoc, (b) its not used for anything else, and (c) by adding it the syntax has a more definite (and we think clearer) style than just allowing one set of brackets to flow into another set of braces.Stephen Colebournenoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-34103992125646320882007-02-28T00:33:18.000+00:002007-02-28T00:33:18.000+00:00I really like this proposal, it feels very Java-is...I really like this proposal, it feels very Java-ish. I think, loops should be supported (do you still plan on doing so?). Maybe like this: super.return (or something similar) is for non-local returns from the method and break/continue throw exceptions that can be caught by loop implementors. A non-local return is simulated (as usual) via an exception.<br /><br />I think Java could really win back a lot of people from the Python, Ruby and Smalltalk camps with first-class methods, map/list literals, multi-line strings and a few clean-ups in the standard library (negative collection indices, anyone?).Axel Rauschmayerhttp://www.pst.ifi.lmu.de/~rauschma/noreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-5594457067435450102007-02-27T22:15:05.000+00:002007-02-27T22:15:05.000+00:00Ok, about my "untyped this" question, do...Ok, about my "untyped this" question, don't know what got me confused but I had the idea, while reading the proposal, that the method reference could actually somehow come from a different instance so that's why I couldn't figure out what "this" meant at all times. But it was just me, thx for the correction :-)<br /><br />Is it maybe possible to give some kind of list of things that you do and do not support compared to the other two proposals? Just to have a clear understanding what we would gain and what we would lose (in your opinion) if this was ever implemented over one of the others.Quintessenoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-41575295530177964702007-02-27T22:02:03.000+00:002007-02-27T22:02:03.000+00:00Stefan,
I do realize that shifting too much to th...Stefan,<br /><br />I do realize that shifting too much to the compiler can be undesirable, however, the goals lately appear to achieve just that. With the new initialization syntax by Peter von der Ahe (HashMap.new()) (which I like a lot) and just in general, to alleviate the typing (new properties proposals, etc).<br /><br />Of course some people do not like cutting away Java's verbosity (which has its place to a degree).<br /><br />My main gripe - and I regret coming across with perhaps too much sarcasm or harshness - is the (subjective) uglification of a good looking language with introduction of symbols which can otherwise be expressed with some context and/or existing symbols.<br /><br />As for name clash (btw use of int was intentional, Integer is too long and what I wrote was not valid Java code anyway), it can again be resolved with some context.<br /><br />In my opinion, a class member initialized with a reference to a method should override the selection of the referenced method of same type. This may appear counterproductive and/or unintuitive in light of the recent discussion about properties (where methods shadow variables of same name, etc), but it carries a nice attribute in that actual method reference can be reassigned without changing the calling code.<br /><br />(pseudo-code)<br /><br />Test {<br /> (List) method = method;<br /> void method(List l) {}<br />}<br /><br />When passing the 'method' literal, compiler will select the field if one exists in the provided lexical scope, otherwise it will attempt to create a reference to a method.<br /><br />If the field is selected, then the actual reference creation is manual, and compiler will simply assign the field to the target method parameter, which can then be invoked using regular invocation syntax.<br /><br />The field can be reassigned to another method without changing the calling site.<br /><br />I'm still pondering the implications of this approach, but its at least something to think about. (for me at anyway)<br /><br />Thanks for your thoughts.<br /><br />P.S.<br /><br />The field in this discussion is not a regular member, it is essentially a pointer to function, and as such should not be seen as "giving preference to a static entity versus a dynamic entity, such as a regular method". Since it is a pointer to method, its end result upon invocation is the execution of the pointed-to method.jnicehttp://niceforj.comnoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-51986178134622724972007-02-27T20:46:54.000+00:002007-02-27T20:46:54.000+00:00Stefan,
My suggestion was not intended to apply o...Stefan,<br /><br />My suggestion was not intended to apply outside the case of multiple abstract methods. I think of it as a natural expansion of what you're proposing for that special case, not a replacement.<br /><br />As for being out of scope, the same argument could also be made against automatic generation of null methods by the compiler. ;)<br /><br />I do want to voice some concerns on the new "this" binding. It may shorten the code but I don't think it simplifies it. People already understand that "this" refers to the object that owns the currently executing method--including in anonymous inner classes. Having "this" bind differently, depending on whether I use the old syntax (anonymous inner class) or the new syntax (anonymous inner method) is inconsistent and bound to cause confusion.<br /><br />For example: Let's say I decide to update some existing code to use the new syntax. So I go through and convert each anonymous inner class, removing the now unnecessary boilerplate. I expect this code to work exactly the same as before. This cannot be guaranteed under the terms of your proposal.<br /><br />I appreciate the savings in typing and the improved readability but I don't think it is worth the confusion.Matthew Hallhttp://paperclips.sourceforge.net/noreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-77807852729274762452007-02-27T19:31:39.000+00:002007-02-27T19:31:39.000+00:00jnice, our goal was to keep the new method element...jnice, our goal was to keep the new method elements visible, clear, and (hopefully) not introduce new sources of error-prone code. The line:<br />(List) fuseMethodTwo;<br />already is valid Java code by now (I changed to Integer, as you cannot have primitives as generic types) representing a cast to a List of Integers (although, it might not make sense to not assign to something). Your ideas are ok, but shift too much inference to the compiler, which makes it difficult to understand for a developer.<br />Imagine you introduce the above static variable to Test and, at the same time, have the method:<br />void fuseMethodTwo(List);<br />Which one will the compiler choose on referencing to Test.fuseMethodTwo? Or would it cause a name clash?<br /><br />Matthew, the syntax you propose was included in some earlier draft of our proposal and referred to as "Concise Instance Construction". It does not directly fit into the proposal, though, as it is not about methods. A CIC would also add further context to the method, namely the instance you are creating (binding of "this"), and is not really far from writing an anonymous class (merely only missing a "new" and a set of braces).<br />I don't say it may not be worth to be introduced, but definitely is out of scope for our proposal.<br /><br />Cheers.StefanStefan Schulzhttp://jroller.com/page/jaddanoreply@blogger.comtag:blogger.com,1999:blog-741750605858169835.post-6437620182147550232007-02-27T17:37:13.000+00:002007-02-27T17:37:13.000+00:00I really like this proposal. The syntax is very c...I really like this proposal. The syntax is very clean and still feels like Java.<br /><br />The only thing I don't like is the automatic generation of null methods (in the MouseListener example). Something just doesn't feel right about it. To me one of the beauties of Java is that its syntax is very clear, there's not a lot of compiler magic you have to worry about. It's one of the reasons I don't like autoboxing and unboxing--yes, it's convenient, but if you get your parameters mixed up you can accidentally call the wrong method, since the primitive is automatically boxed into an object. Before autoboxing this would have been caught by the compiler.<br /><br />Rather than automatically generating the missing methods, I think it would be more natural to add a syntax for subclassing and overriding a single method. In the MouseListener example, we would subclass MouseAdapter:<br /><br />MouseListener listener = MouseAdapter#mouseClicked(MouseEvent evt) {<br /> ...<br />};<br /><br />Note that this syntax overlaps with the syntax for referring to static methods e.g. Math#min(int, int), however the presence of the code block should be enough to clarify the programmer's intention to subclass.<br /><br />This makes what you're doing explicit, without much burden. It also allows you to program to the interface (listener is a MouseListener reference instead of MouseAdapter) but with the ability to explicitly specify the base class. I can think of dozens of cases where this usage would be useful for me.<br /><br />The DefaultListCellRenderer example could then be changed to:<br /><br />ListCellRenderer renderer = DefaultListCellRenderer#getCellListRendererComponent(JList list, Object value, int index, boolean isSelected, boolean hasFocus) {<br /> ...<br />};<br /><br />If the superclass requires arguments in the constructor, you could specify them the same way as with anonymous inner classes:<br /><br />MyInterface object = MyAbstractImplementer(arg1, arg2)#interfaceMethod() {<br /> ...<br />};Matthew Hallhttp://paperclips.sourceforge.net/noreply@blogger.com