On Thursday, September 15, 2011 5:55:28 PM UTC-4, Ricky Clarkson wrote: > > I disagree that the C# example is contrived, having accidentally made the > mistake they show. >
Still, this is just an isolated anecdote. I have already made my fair share of bugs, in virtually all categories, since I started programming... and I won't claim that Java's traditional for loop is broken because I have made some off-by-one errors. Any kind of bug can appear to be much more important (easy to make) when you are not very familiar with some new programming idiom. I'm not a C# guy but I know that closures are a relatively new feature, added only in C# 3.0, so I suppose their use is not as intense as in systems that adopted closures since day zero in the language and core libraries. But let's invert the perspective: suppose some Smalltalk diehard comes to us and claims that any language that doesn't have a numerical tower is broken, because without that, programmers will make bugs like overflow or unintended loss of precision all the time. Maybe this is true for a Smalltalker that is transitioning to languages like C/C++/C#/Java, but it's certainly not true for a veteran in those languages. I for one, sincerely have no memory of *ever* having made an integer overflow bug in my entire professional life. (Not really bragging - I've started learning programming at 13yo, with Assembly no less, so my instincts for low-level decisions like picking the correct numeric precision for each variable were already very sharp by the time I started working...) There is zero evidence that I know of, that closure-capture of mutable variables are a significant risk of bugs (higher than reasonable, considering that any language feature allows some kind of bugs). We have 50 years of industry experience since Lisp in 1959+, and I don't think the whole problem was ever discussed at all before Java made the entire concept of closures subject of heated debate for the first time. Paraphrasing the very funny blog http://james-iry.blogspot.com/2009/05/brief-incomplete-and-mostly-wrong.html, "Java has made full closures questionable by not having them..." A+ Osvaldo > > On Thu, Sep 15, 2011 at 9:37 AM, opinali <[email protected]> wrote: > >> Oh got it. I agree that return/break/continue (the so-called non-local >> returns) are a problem, although they are only such a big problem if we >> don't want to improve the classfile/bytecode/VM specs to have a specialized >> mechanism for this in order to not depend on the exception hack. >> Unfortunately, too many Java language improvements are tainted by this >> criteria of desiring as few changes to the VM platform as possible. >> >> But I don't see this problem as a justification for the separate issue of >> mutable capture. We're talking about completely distinct features. Capturing >> mutable local vars has a trivial implementation (lifting the variable to a >> field of the closure object, visible both from the defining scope and from >> the closure code). >> >> I read the C# example, and it didn't impress me; it's not just contrived, >> it's a VERY contrived example. You can easily build such examples to show >> that every single feature of any language can be involved in some >> incorrect/confusing code. In the example, the bug is not caused only by >> mutable capture - it's mostly likely caused by programmer's failure to >> realize that the execution of the closure will be deferred. This is the most >> fundamental property of a closure if compared to a regular block or >> expression inside the containing method; developers can shoot themselves in >> the foot all day long if they fail to be aware of when the code of closures >> is actually executed, without even needing mutable capture. Taking a page >> from lambda-dev (from Neal Gafter IIRC), closures (including mutable >> capture) are an ages-old feature which use is time-proven by numerous >> languages. >> >> A+ >> Osvaldo >> >> >> On Thursday, September 15, 2011 3:10:39 AM UTC-4, Ricky Clarkson wrote: >> >>> What I perhaps didn't make clear is that I'm talking about taking some >>> code that's part of a method, and then making it part of a lambda within >>> that method. >>> >>> E.g., in some imaginary syntax: >>> >>> void foo() { >>> if (Math.random() < 0.5) >>> return; >>> System.out.println("Foo"); >>> } >>> >>> void foo() { >>> timeHowLongThisTakes( () => { >>> if (Math.random() < 0.5) >>> return; >>> } ); >>> System.out.println("Foo"); >>> } >>> >>> Return is one example; others are break, continue and throw. The meaning >>> is not kept if you wrap that code into a new Runnable() { public void >>> run()... } >>> >>> Gafter's original, well thought-out, argument was that wrapping code in a >>> closure should not affect its meaning, which makes a lot of sense, but the >>> actual mechanisms needed to do that at least before engaging the VM team >>> are >>> odd, it used exceptions under the hood to manage these things, and there >>> were objections to that. And (answered) questions about what return; would >>> mean if the lambda were to be executed on a different thread to the >>> surrounding method. >>> >>> I think at this stage it makes sense to stick with a restricted lambda >>> and get that in stone. The current proposal does not in any way preclude >>> Oracle from adding support for capturing mutable variables, a notation for >>> outer returns, enabling break and continue, and making throws able to be >>> transparent. >>> >>> Regarding mutable variables, I'm sure you read the passage I quoted from >>> the C# specification and can appreciate that it's not all roses without >>> that >>> limitation. It's not just theoretical, in my brief time on Freenode's >>> ##csharp I noticed it appeared fairly often. Whether it was deliberate 14 >>> years ago I don't think matters any longer. >>> -- >>> Skype: ricky_clarkson >>> >>> >>> On Wed, Sep 14, 2011 at 11:12 PM, opinali <[email protected]> wrote: >>> >>>> On Wednesday, September 14, 2011 9:15:30 PM UTC-4, Ricky Clarkson >>>> wrote: >>>>> >>>>> I think the problem is that whenever you start trying to make sure that >>>>> all code wrapped in a lambda behaves the same as code not wrapped in a >>>>> lambda (return, break, this, continue, throw all behaving the same way) >>>>> you >>>>> get into odd territory and someone picks up on it and reacts. >>>> >>>> >>>> This seems to be mixing things. Code inside an inner class certainly >>>> behaves exactly like all other code; specifically, you can define mutable >>>> variables and you can assign to these variables. So the typesystem inside >>>> the "closure" is exactly the same. Which in this case, makes inner classes >>>> even more broken, it would be cleaner if inner classes had been designed >>>> as >>>> a pure-functional subset of the language that couldn't do destructive >>>> assignments at all. >>>> >>>> See Guy Steele's comments about inner classes: http://people.csail.** >>>> mit.edu/gregs/ll1-discuss-**archive-html/msg04044.html<http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg04044.html> >>>> - >>>> Java was initially planned to have closures, including mutable capture, >>>> but >>>> that was cut from JDK 1.0 and when they introduced inner classes in 1.1, >>>> it >>>> didn't have mutable capture and the reason was just "users feared the >>>> performance cost" - maybe reasonable concern with JDK 1.0's VM with the >>>> crappiest GC ever, but an obsolete issue since HotSpot, not to mention >>>> that >>>> other language features already ignore any concerns (performance or other) >>>> of implicit allocations, like Steele points. Also, James Gosling referred >>>> to >>>> inner classes as an `"uncomfortable compromise ... that didn't really >>>> solve >>>> any problems": >>>> http://blogs.**oracle.com/jag/entry/closures<http://blogs.oracle.com/jag/entry/closures>. >>>> >>>> And a third "father of Java", Bill Joy, also wanted full support for >>>> closures since day one: http://www.blinkenlights.** >>>> com/classiccmp/javaorigin.html<http://www.blinkenlights.com/classiccmp/javaorigin.html> >>>> >>>> TL;DR: The mutable capture limitation of inner classes was NOT >>>> introduced by design. Not one of the original Java designers are proud of >>>> inner classes. Any attempt to justify this limitation as something that >>>> makes sense with good language design principles, is rewriting history - >>>> "it's not a bug, it's a feature". >>>> >>>> A+ >>>> Osvaldo >>>> >>>> >>>> >>>>> >>>>> Scala actually does some* of what Gafter's original proposal did >>>>> regarding control, and the reality is I've only ever noticed when I >>>>> explicitly tested it to see if I could observe a NonLocalReturnException. >>>>> However, I don't think we're going to win that fight, and C#'s level of >>>>> lambda support really is a marked improvement over what we have in Java >>>>> today. >>>>> >>>>> * some, because Scala doesn't have continue or break. >>>>> >>>>> -- >>>>> Skype: ricky_clarkson >>>>> >>>>> >>>>> On Wed, Sep 14, 2011 at 8:38 PM, opinali <[email protected]> wrote: >>>>> >>>>>> Hi clay, >>>>>> >>>>>> Ok let's be more formal now. I agree with option A below (instead of >>>>>> B); "closure" is very well defined in numerous classic CS books as the >>>>>> pairing between a function and an environment that provides bindings to >>>>>> its >>>>>> free variables. The concept comes from lambda calculus where functions >>>>>> can >>>>>> only exist in the form of functions, so I understand view B - but >>>>>> practical >>>>>> programming languages are very distant from pure lambda calculus; only >>>>>> in >>>>>> the Lisp family (I think) they are really the same because functions are >>>>>> just sugar for a lambda - i.e. a function is a lambda that is bound by >>>>>> some >>>>>> name to some environment. But there are many other languages, including >>>>>> Java, where this doesn't happen, unless you interpret the concept of >>>>>> "environment" as something more general, including the all scoping >>>>>> rules, >>>>>> classloaders, etc. IMHO this is going a bit too far, I'm happy with a >>>>>> definition of closures that does not depend from lambdas. >>>>>> >>>>>> Now back to my points 1/2, let's try to refine the discussion to its >>>>>> true essentials: the major point of debate is whether Java's closures >>>>>> (with >>>>>> or without Java 8's lambdas) are "good enough". So let's try to define >>>>>> what >>>>>> constitutes good enough. I am a strong advocate of very powerful >>>>>> closures; I >>>>>> was rooting for BGGA with its non-local returns, control >>>>>> abstractions..., >>>>>> but I concede that these features are only necessary to extend Java's >>>>>> paradigm, allowing significant new idioms. But it's fair to require for >>>>>> "good enough", an implementation of closures that is at least sufficient >>>>>> to >>>>>> get along with the *existing* language paradigm, idioms and other >>>>>> features >>>>>> (typesystem, OO model etc.) - anything better that that is a bonus. If >>>>>> we >>>>>> agree on this, then "good enough" boils down to orthogonality. And the >>>>>> current support for closures (without capture of mutable variables) is >>>>>> clearly NOT orthogonal, because destructive assignment is a HUGE part of >>>>>> the >>>>>> Java language. No amount of fancy functional-esque frameworks or >>>>>> conventions >>>>>> can change this simple fact. It's broken since JDK 1.1's introduction of >>>>>> inner classes, and it will be broken in Java 8's even with lambdas, nice >>>>>> syntax, and tons of new or retrofitted APIs to use closures/lambdas. >>>>>> >>>>>> A+ >>>>>> Osvaldo >>>>>> >>>>>> >>>>>> On Tuesday, September 13, 2011 7:58:02 PM UTC-4, clay wrote: >>>>>>> >>>>>>> From the best I can gather, the principle disagreement is >>>>>>> definitions. >>>>>>> There are three views in this thread: >>>>>>> >>>>>>> A) Java has closures. Closures, by definition, do not necessarily >>>>>>> require lambdas or first class functions, which Java doesn't have. >>>>>>> B) Closures, by definition require lambdas and/or first class >>>>>>> functions, so since Java doesn't have those, it can't have closures. >>>>>>> C) Pure closures, by definition, don't limit you to accessing only >>>>>>> "final" variables, so the anonymous class functionality doesn't >>>>>>> qualify as a closure. >>>>>>> >>>>>>> Osvaldo, >>>>>>> >>>>>>> Regarding point #1: First, I think you mean anonymous classes, not >>>>>>> inner classes. Secondly, I'd stress that anonymous functions >>>>>>> (lambdas) >>>>>>> are a distinct feature from closures. A language can have one >>>>>>> feature >>>>>>> but not the other. Java has closures, but not lambdas. And I can't >>>>>>> think of them off the top of my head, but I'm sure there are >>>>>>> languages >>>>>>> with lambdas that don't close over the defining environment, and >>>>>>> therefore don't have closures. >>>>>>> >>>>>>> Regarding point #2: I'm not sure what you are getting at here. Sure, >>>>>>> Java is behind the pack in terms of immutable programming and >>>>>>> functional programming support, but you can use third party libraries >>>>>>> >>>>>>> like Functional Java and adopt that style as a programmer. Only a few >>>>>>> >>>>>>> languages like Haskell really force those concepts on you and >>>>>>> guarantee safety from mutability issues. In Scala, immutable code and >>>>>>> >>>>>>> functional programming still requires programmer compliance and it's >>>>>>> easy to write imperative mutable code. Regardless, this seems like a >>>>>>> tangential issue. >>>>>>> >>>>>>> Kevin, >>>>>>> >>>>>>> I'm not arguing that Java has closures because you can achieve the >>>>>>> same goals without real language level support. I'm arguing that >>>>>>> closures are inner functions/methods that can access variables from >>>>>>> the outer function/method scope, and the Java language syntax >>>>>>> completely supports that. You are claiming that closures require >>>>>>> lambdas by definition, and I disagree with you on that definition. >>>>>>> Java doesn't have lambdas. Sure, you can achieve similar results with >>>>>>> >>>>>>> anonymous classes, but the Java language itself doesn't provide >>>>>>> lambdas. >>>>>>> >>>>>> -- >>>>>> You received this message because you are subscribed to the Google >>>>>> Groups "The Java Posse" group. >>>>>> To view this discussion on the web visit https://groups.google.com/d/ >>>>>> **ms**g/javaposse/-/53VocU4KYT0J<https://groups.google.com/d/msg/javaposse/-/53VocU4KYT0J> >>>>>> . >>>>>> >>>>>> To post to this group, send email to [email protected]. >>>>>> To unsubscribe from this group, send email to >>>>>> [email protected]**. >>>>>> >>>>>> For more options, visit this group at http://groups.google.com/** >>>>>> group**/javaposse?hl=en<http://groups.google.com/group/javaposse?hl=en> >>>>>> . >>>>>> >>>>> >>>>> -- >>>> You received this message because you are subscribed to the Google >>>> Groups "The Java Posse" group. >>>> To view this discussion on the web visit https://groups.google.com/d/** >>>> msg/javaposse/-/Fff1UZB3f5EJ<https://groups.google.com/d/msg/javaposse/-/Fff1UZB3f5EJ> >>>> . >>>> >>>> To post to this group, send email to [email protected]. >>>> To unsubscribe from this group, send email to >>>> [email protected]**. >>>> For more options, visit this group at http://groups.google.com/** >>>> group/javaposse?hl=en <http://groups.google.com/group/javaposse?hl=en>. >>>> >>> >>> -- >> You received this message because you are subscribed to the Google Groups >> "The Java Posse" group. >> To view this discussion on the web visit >> https://groups.google.com/d/msg/javaposse/-/W8qWzFi4XtoJ. >> >> To post to this group, send email to [email protected]. >> To unsubscribe from this group, send email to >> [email protected]. >> For more options, visit this group at >> http://groups.google.com/group/javaposse?hl=en. >> > > -- You received this message because you are subscribed to the Google Groups "The Java Posse" group. To view this discussion on the web visit https://groups.google.com/d/msg/javaposse/-/uFBxCZKH2FYJ. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/javaposse?hl=en.
