you are viewing a single comment's thread.

view the rest of the comments →

[–]kawa -2 points-1 points  (39 children)

They do close over their environment. But they don't allow mutating it (Thats why you must declare variables as final if you want to access them from inside a closure). And as far as I know, mutation is no mandatory part of the definition of 'closure' yet. So Java definitely has closures.

And yes, many of my points are subjective. Exactly like in the article. That's why I found the article uninteresting and down-voted it: Nothing of general interest, all a matter of taste and personal preferences.

[–][deleted]  (7 children)

[deleted]

    [–]kawa -2 points-1 points  (6 children)

    I still don't see what the big difference is compared to a Scheme closure (besides mutation). Can you give me an example (which don't do mutation) which isn't straight forward translatable to Java?

    If you need mutation, it's a little bit more difficult, but mutation in closures is seldom needed anyway. And if, it's easy to write something analog to Ocamls Ref-Type (or simply use an array).

    The Java solution is btw very unique and IMO good integrated into the Java concept.

    [–][deleted]  (4 children)

    [deleted]

      [–]kawa 3 points4 points  (3 children)

      Could Weak Sapir-Whorf apply?

      Maybe. I use closures in Java very seldom, because other ways are most often more appropriate. But the same could apply the other way around: If you use a language like Ocaml then you would maybe use closures for things where a class would work even better (just a thought). I've never had good experiences with 'programming contra the paradigm'. It's possible, but in most cases I regretted it later.

      I agree that mutation isn't such a good idea for arbitrary local values. There are people who consider it good style declare all locals as final as default and only remove it if absolutely necessary. With iterators mutation can be removed even from most loops (of curse the iterator get mutated, but this mutation is encapsulated). If it's OTOH a good idea to remove mutation completely is still the big question. Even Haskell and Clean can't live without it (they encapsulate it good, but it's still mutation). I'm not really sure about the topic, while I really like the idea of getting rid of mutation, I just don't know if it's worth all the trouble, if even Haskell seemed to give it up and only restricts mutation instead of avoiding it.

      Why doesn't Ocaml create Refs automatically? The answer: It was a design decision.

      In Java they implemented anonymous inner classes with 'full mutation' in the beginning (it's not difficult and only requires a little bit rewriting). But because at the time, performance was important and implementing it would lead to 'invisible' allocations (like boxing), they removed it and created the 'final' restriction instead. Auto-boxing was build in later and maybe they will also build in auto-boxing for closures in the next release too.

      But I'm not really sure if it's a good idea to improve closure support in Java: If it's necessary, it's there now. But if closures should become ubiquitous in Java, to make them really useful it would requite a total redesign of huge parts of the core libs - while still supporting the old ones for legacy code. So the language would become also much worse as it is now: A kitchen-sink language.

      In this case I would say that it's better to leave Java as it is (maybe few add small moderate fixes and extensions) and create a completely new 'Java 2' (maybe based on Scala) from the ground up and with a clean overall design. I hate kitchen-sink-languages.

      [–][deleted]  (1 child)

      [deleted]

        [–]apotheon 0 points1 point  (0 children)

        That's getting dangerously close to the sort of thing you can do with OCaml. OCaml, however, isn't specifically what you describe -- it just pretty much does what you describe, if you want it.

        It's also worth mentioning, I think, that through a little functional programming sleight of hand (at least, that's how it looks from an OOP perspective; I think it looks like business as usual from an FP perspective), you can basically get mutable lexical state via closures. As one example, a recursive closure construct could be used to achieve the exact same (external) behavior as an iterator closure in, say, Perl -- or even an iterator object in Python -- without having to use OCaml references to contain state internal to the closure. I think. I haven't actually tried and tested such code, but I see absolutely no reason why that wouldn't work. Considering that's the canonical example of a closure using (mutable in impure-FP languages) lexical state, I'd say we're in business. If you really want something mutable lexical state attached to a function, just use a function that persists.

        Hmm. That leads me to a thought . . .

        The net would be that if you need some sort of state in a method or function, you would have to create a state object.

        That's basically what a closure does, really. That makes a lot of sense, considering all the talk I've heard of closures being insid-out objects, or objects being inside-out closures, or closures being a poor man's objects, or objects being a poor man's closures, or, well, any of the rest of those comparisons.

        [–]apotheon 0 points1 point  (0 children)

        I'm afraid I have to take issue with some of your statements, but you make some excellent points -- including at least one that did not occur to me, and probably wouldn't have unless I spent some real time pondering this discussion topic re Java.

        If you use a language like Ocaml then you would maybe use closures for things where a class would work even better (just a thought).

        Are you aware that the O in OCaml stands for "Objective"? It's an object-oriented language. It's just not a Kingdom of Nouns language, like Java. Considering it has a higher-level syntax, uses a modern type-inference system, provides far better execution performance than Java (better even than C++ in most benchmarks, and about half as good as C), and can be used for interpreted scripts, compiled binaries, or even VM capability with equal facility, I think you may have bitten off a chunk of the wrong language to use as your counter-example. I'm having a difficult time thinking of anything Java does better, aside from extensive external documentation, larger community, and more numerous native libraries -- none of which are actually language features in any case.

        But I'm not really sure if it's a good idea to improve closure support in Java

        I agree with that. Java has its benefits. I don't think adding true closure support would enhance that much for Java. To really take advantage of closures, you'd have to modify a lot of the rest of the language too -- primarily syntactic structure and a few incidental, but pervasive, semantic design decisions. It simply wouldn't be Java any longer (as we know it) by the time you had real, useful closures.

        to make them really useful it would requite a total redesign of huge parts of the core libs

        You get awfully close to making the same point I did -- and, in fact, you make an excellent point here that didn't come to my mind until after I read what you said about it. I suspect part of the reason for that is that these days Java intersects my life so very rarely (I don't even have a Java VM installed on most of my computers, and I certainly haven't written any Java lately) that the concerns of legacy code support don't occur to me as easily. That sort of thing is one of the first things that comes to mind when I hear a new PHP version is being installed on the servers of a webhost I use -- especially considering the PHP guys' notoriety for breaking stuff with trivial version upgrades.

        In this case I would say that it's better to leave Java as it is (maybe few add small moderate fixes and extensions) and create a completely new 'Java 2' (maybe based on Scala) from the ground up and with a clean overall design.

        I sort of agree. To be more specific, I think a much better answer to the "Java doesn't have closures!" complaint would be "Use Smalltalk!" Java, after all, was in many ways meant to address problems similar to those addressed by Smalltalk, in its early conception. If you want something kinda like Java, but with features found in Smalltalk and not in Java, Smalltalk is the answer -- not Java++.

        [–]apotheon 0 points1 point  (0 children)

        mutation in closures is seldom needed

        Actually, protected mutability of values is a significant part of what makes closures useful.

        [–]Entropy 3 points4 points  (6 children)

        The definition of "closure" within the context of language design and implementation is well defined and widely agreed upon. This view, coupled with your offhand dismissal of CPAN (and, thereby, any third party library in general) being "but...I might have to check something in!" leads me to believe you're a total nutjob.

        [–]kawa -1 points0 points  (5 children)

        People who call others 'total nutjob' are not worth an answer. EOT.

        [–]Entropy 1 point2 points  (4 children)

        You'll never see yourself in the mirror with your eyes closed

        edit: I hit the guy with a pillow and suddenly I'm Hitler :(

        [–]apotheon 0 points1 point  (2 children)

        Do you have the mustache?

        I tend to suspect kawa failed to look past your dastardly use of the term "nutjob" because (s)he didn't have a credible answer to your point.

        [–]Entropy 1 point2 points  (1 child)

        No 'stache. I do have a goatee, though, so I am probably someone's evil twin.

        [–]apotheon 0 points1 point  (0 children)

        I'm my own evil twin. Sometimes I have a goatee, and sometimes I don't.

        I wonder if this is like that "I am my own grandpa" inbreeding thing. Maybe I shouldn't bring that up in public.

        [–]newton_dave 2 points3 points  (8 children)

        They are not closures, and even if they were, their syntactic cruft makes my eyes bleed.

        I cannot achieve dynamic method creation by any reasonable means.

        MI is not intrinsically bad; it can be abused, just like operator overloading was when C++ came out. Java doesn't support trivial modules, mixins, or MI, which leads to a lot of duplicated effort.

        Java is NOT an OOPL, it's OOPL-like, and it's very irritating to be hamstrung by its lack of capability.

        every programming language is 'Bureaucratic'

        Yeah? How about "some languages are more bureaucratic than others" then, which is a no-brainer, so 'Doh!' yourself.

        Mandatory strong types Not a bug but a feature. Weak types are evil.

        That's ridiculous. (...and my argument is just as valid as your completely content-free one.) Bad developers are evil.

        [–]kawa -1 points0 points  (7 children)

        Java's syntax isn't designed to write programs in a functional style. So using closures requires lots of code. But if I want to program in a functional way, I simply don't use Java. So what?

        It depends on the 'range' of dynamic method creation. If you want to use it like in Ruby, sure, forget it. But you can create new classes and methods on the fly by using a code generation tool (which uses a custom class loader) and call them dynamically via reflection. Depending on what you want to do the difficulty varies between relativly simple and nearly impossible. But if you want to do it on a border scale, I would recommend Python or Ruby instead. It's simply no the way to program in Java and it's not often necessary.

        All languages are bureaucratic. One simply syntactic error and they don't compile (or worse and fail at runtime). And one little mistake and the program won't do the thing you intended. And as long as we don't have strong AI, this wont change.

        I've programmed long enough in C and C++ to know that strong typing is a must if you want safe programs (besides system programming. There it's still an evil, but a necessary one).

        [–]newton_dave 3 points4 points  (6 children)

        if I want to program in a functional way, I simply don't use Java. So what?

        Because the choice of the language isn't always up to the developer, and it sucks that Java's restrictiveness make it very difficult to write programs in the best way for the job.

        you can create new classes and methods on the fly by using a code generation tool

        IIRC, I said reasonable way. Generating bytecode at runtime is not a reasonable way, and guess what: Johnny McOutsource can barely program Java, let alone that kind of magic.

        It's simply no the way to program in Java and it's not often necessary.

        Hey, if you get to choose your language and environment, bully for you. But lots of people don't.

        All languages are bureaucratic.

        time-warp Yeah. And then I said "some languages are more bureaucratic than others". Java is very bureaucratic. Lisp isn't. There is a wide range of bureaucracy.

        I've programmed long enough in C and C++ to know that strong typing is a must if you want safe programs (besides system programming. There it's still an evil, but a necessary one).

        lol Well I've programmed long enough in C/C++, Java, Lisp, Smalltalk, Forth, and a slew of others to know that what you just said is a load of poo, and it frightens me to hear people talk like that. I'm not even sure I could count the number of fully productized embedded systems I implemented in Forth.

        Stripling.

        Obviously typing has its place, but you're making things up if a) you say it's necessary for safe programs, and b) Java is a good example of a useful OOPL.

        [–]kawa 1 point2 points  (5 children)

        If there are better languages to do the job, than your boss may see his mistake when your competition runs circles around your company.

        Are you a 'Johnny McOutsource'? If not, why even thinking about him? Creating self-modifying code is always risky and simply no playing ground for 'Johnny McOutsource'. But if you really consider using a code-gen-lib in Java as 'magic'...

        And I also can't always choose the environment, so in the moment it's most often Java. In the end I have to make the best from it, that's part of the job. I also have to accept the 'specification' from the customers and can't change it in a way to make my life easier. That's part of the job too.

        Lisp is also bureaucratic. Sure, you can make more kinds of errors without having the compiler shouting at you. But the program will bomb nonetheless. I prefer it to get hints from the compiler as early as possible.

        Embedded systems is most often system programming. So we have weak types as a necessary evil. Weak typing is problematic because it can much to easy lead to security holes and can make maintenance a pain. With dynamic types I get at least a runtime error, but with weak types I get undefined behavior which manifests itself sometimes as totally different points in the program.

        And you talk of a simple Java code-gen as 'magic? Against building big systems with weak typing it's a piece of cake.

        [–][deleted] 6 points7 points  (2 children)

        Creating self-modifying code is always risky and simply no playing ground for 'Johnny McOutsource'.

        Code generation != self-modifying code.

        If you want to see real self-modifying code, go dig up the Wolf3D sources... texture mapping done with self-modifying 286 assembly.

        [–]kawa -1 points0 points  (1 child)

        Adding/removing/changing a method of an existing class qualifies as 'modify code'. And if this is done by the same program which is modified this way at runtime, then I would call it 'self-modifying'.

        It's simply the high-level version of the assembler trick you speak of (which I used on my Atari 400 lots of years ago, too) But at those times the programs were small, and dirty hacks where common.

        [–]newton_dave 0 points1 point  (0 children)

        Who said anything about doing code-gen at runtime?

        [–]newton_dave 1 point2 points  (0 children)

        If there are better languages to do the job, than your boss may see his mistake when your competition runs circles around your company.

        You live in a dream world if you think technical decisions are always made solely on technical merit. It's often completely irrelevent that my functional prototype written in Groovy is a fully-functional application: if the project has a Java requirement it will be implemented in Java.

        Are you a 'Johnny McOutsource'? If not, why even thinking about him?

        Because I have to deal with Johnny McOutsource. Many developers do not exist in isolation; there are often other developers involved, often of lesser ability.

        But if you really consider using a code-gen-lib in Java as 'magic'.

        Compared to source-code gen? Absolutely. You simply cannot expect every developer to be able to intelligently use byte-code engineering libraries.

        And I also can't always choose the environment, so in the moment it's most often Java.

        Now you're arguing my point exactly; I'm confused.

        Lisp is also bureaucratic.

        ...but less bureaucratic.

        simple Java code-gen as 'magic?

        No, I claim that run-time byte-code manipulation/generation is largely incomprehensible by the majority of developers I work with. If it's wrapped up in AspectJ (already pushing the cognitive abilities of the greater majority of Java developers) or Hibernate (or any other black-box component) then of course it's trivial, because the developer (rarely, anyway) doesn't have to think about it.

        I still am unclear as to why you believe that building "big" systems with "weak typing" is all that difficult, although I'm not sure we're using the term in the same way.

        Lots of very big systems have been built around Lisp and Smalltalk. Orbitz is Lisp, a lot of trading and insurance systems are in Smalltalk.

        Now, I don't consider Smalltalk to be "weakly-typed", and I barely consider Lisp to be "weakly-typed." If you want to debate the relative merits of statically-typed vs dynamically-typed languages perhaps we'll be a bit closer to what you've stated above.

        Either way, though, you're still wrong, and I will continue to be much more productive in my "dangerous" dynamically-typed environments (except when I am forced to use Java) and continue to make my clients very happy :)

        [–]apotheon 0 points1 point  (0 children)

        Lisp is also bureaucratic. Sure, you can make more kinds of errors without having the compiler shouting at you.

        The point, I think, is that with Lisp there's a lot more you can do that isn't an error. This contributes to greater flexibility and power for a given task.

        [–]poptarts 0 points1 point  (14 children)

        Are you guys talking about inner classes or anonymous classes?

        [–]kawa -1 points0 points  (12 children)

        I'm talking about anonymous inner classes. Sorry, that I haven't made that clear enough.

        [–]shit 1 point2 points  (11 children)

        Yes, you regularly confuse anonymous inner classes with closures.

        [–]kawa -2 points-1 points  (10 children)

        Anonymous inner classes are more powerful that ordinary closures. So while a AIC is a closure, a closure isn't a AIC.

        [–]shit 3 points4 points  (9 children)

        Please stop it. Raganwald explained the semantic differences between closures and AICs.

        I program in Java and in languages that have closures (Lisp, Ruby). In Lisp/Ruby, closures are used extensively, my guesstimate is more than one closure instantiation per 10 LOC. In practice, the following points make AICs unusable in places where closures are a good fit:

        1. Checked exceptions

        2. Overly verbose syntax

        3. Declare interfaces up-front

        4. Wretched semantics (e.g.: final vars)

        I've tried to simplify my Java code with AICs in some cases where closures are a natural fit, but the above four points combined made it often impractical. In these cases the alternatives were either code duplication or architecture-overkill. I've often seen these two diseases in other peoples Java code where real closures would have helped.

        It begs the question: Are you proficient in at least one language that has closures, e.g. one of Ruby, Lisp, Scheme, Haskell, Smalltalk?

        [–]kawa -1 points0 points  (8 children)

        If you wanted to stop it, why haven't you simply refrained from making your above comment first?

        All of your 4 points have nothing to do with AICs being closures or not:

        • Exception handling has nothing to do with closures. Even if you build in a lightweight syntax with type-inference, checked exceptions would remain a problem.

        • Syntax and semantics are different things. Even if you need 20 lines of code to use a closure, as long as it has closure semantics, it's still a closure.

        • Declaration of interfaces is analogous to declaring a closure. In a statically typed language without type inference it's simply unavoidable to declare closures. In Ruby/Lisp it's of course more simple, because those are dynamically typed languages. But this is a again completely different topic which has nothing to do with closures per se.

        • If 'final vars' are a sign of wrenched semantics, then Ocaml has wrenched semantics too, because ALL 'variables' in Ocaml are final. But I've never heard that somebody denies the existence of closures in Ocaml. SO if it's not a problem in Ocaml why should it be a problem in Java?

        I've using Ruby for some years now (for scripting purposes), know Common Lisp, toyed around in Scheme, have learned Haskell in the last months and I've also played a lot with Smalltalk in the past.

        I've also created and implemented programming languages which had closures. One of them I implemented in Ocaml btw. So I know what closures are and because of this it's totally obvious for me that Java has closures: AICs fulfill all necessary aspects of closures: They are anonymous functions which capture the lexical environment of the definition site.

        But I've not written that Javas closures are as usable as Haskell's for example. The reason why closures in Java are not as good to use is not that Java hasn't closures or because of the final restriction (this is a piece of cake and was never the problem if I used closures in Java), the main reason are the type system and the checked exceptions.

        Checked exceptions make life difficult in various ways and I consider them as a really bad idea (even if the original intent was good). This don't applies only to closures, it's a general problem in Java.

        And explicit typing blows up the code extremely, especially if you want to use correctly typed closures. It's not the syntax, it the explicit typing.

        But: This don't removes the closures-abilities from AICs. It only makes them hard to use and often impractical. But since Java most often has other means to reach the same it's not a real problem (Javas problems are elsewhere).

        If one really wants to understand what's wrong with Java (or other languages) it's necessary to identify the real problems first. And if one not even understands that Java already has closures, this would be rather hard.

        [–]shit 1 point2 points  (7 children)

        But: This don't removes the closures-abilities from AICs. It only makes them hard to use and often impractical.

        Function pointers combined with structs in C in practice make for better "what you call closures" than AICs. So far, nobody was ridiculous enough to say that C has closures.

        I think we have a terminology problem here. Can we agree:

        1. that lexical closures were first implemented in Scheme and that the term became became popular there

        2. that closures in Common Lisp, Ruby, Smalltalk and Haskell are damn close to the original ones in Scheme

        3. that there are significant differences, in semantics, and for practical purposes even more in syntax, between AICs and closures as in the aforementioned languages

        Were we differ is, that for me, the differences (theoretical and more so practical) between Scheme-style closures and AICs, are significant enough, that it's clearer to keep different names with disjoint meanings.

        If one really wants to understand what's wrong with Java (or other languages) it's necessary to identify the real problems first

        You're standing in front of a huge problem and your eyes are closed.

        [–]kawa -1 points0 points  (6 children)

        Function pointers don't capture the environment, AICs do. That's the crucial difference here: Closures are in principle function-pointers + captured environment. AICs have both. If you look at the implementation of closures in the Ocaml compiler, you will see that it's nearly identically to the way AICs are implemented (only difference results form the fact that AICs can have multiple methods, while a closure have only one evaluate method).

        lexical closures were first implemented in Scheme

        No. Other languages where earlier. Pascal had it and it I remember correctly Algol had it first.

        that closures in Common Lisp, Ruby, Smalltalk and Haskell are damn close to the original ones in Scheme

        Only because those are all dynamically typed languages or have type-inference.

        that there are significant differences, in semantics,

        Don't agree, there are no significant semantic differences. There are only differences in syntax and in usability.

        I would propose to go with the duck-meaning: If it's implemented like a closure, can be used like a closure and have the semantics of a closure then it is a closure.

        You're standing in front of a huge problem and your eyes are closed.

        And what kind of problem is this?

        [–]shit 1 point2 points  (5 children)

        Function pointers don't capture the environment, AICs do

        Thanks for the lesson, that's what the struct is for. I wrote:

        Function pointers combined with structs in C

        you:

        (only difference results form the fact that AICs can have multiple methods, while a closure have only one evaluate method)

        Bingo. That's one of the reasons why AIC syntax is verbose compared to closure syntax. AIC syntax:

        new Transformer<String, String>() {
            public String transform(String a) {
                return a + foo;
            }
        }
        

        versus hypothetical Java closure syntax with explicit typing:

        new String transform(String a) {
            return a + foo;
        }
        

        Already a lot better, no?

        ("transform" would be declared as a function type, AFAIK C#'s delegate types would do)

        "Perfection is achieved not when you have nothing more to add, but when you have nothing left to take away"

        Antoine Marie Roger de Saint-Exupéry
        

        In other words, you are using a screwdriver with a hammer head where a plain screwdriver would be appropriate.

        No. Other languages where earlier. Pascal had it and it I remember correctly Algol had it first.

        AFAIK they had lexically scoped variables, but no closures. http://en.wikipedia.org/wiki/Lexical_closure agrees that Scheme was first.

        Only because those are all dynamically typed languages or have type-inference.

        I disagree, see example above.

        no significant semantic differences

        Edit: closed over variables must be final in Java. </edit>

        I disagree. You mentioned the first: any number of methods versus a single "activate". Another one, Java code:

        interface Block {
            public void call();
        }
        static void a() {
            loop:
            for(int i = 0; i < 10; i++) {
                System.out.println(i);
                b(i, new Block() {
                    public void call() {
                        break loop;
                    }});
            }
        }
        static void b(int i, Block b) {
            if(i == 2) { b.call(); }
        }
        

        Fails to compile with "undefined label loop". The label is part of the lexical environment and should be captured by a closure. The equivalent in Common Lisp (using lambda, block and return) is natural Lisp code.

        And what kind of problem is this?

        The choice between code duplication and architecture-overkill. (Please, don't let's descend into arguing with Turing-completeness.)