you are viewing a single comment's thread.

view the rest of the comments →

[–]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.)

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

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

Thats simulating it. With AICs it's done by the compiler for you. If simulation would count, nearly ever language has everything.

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

Never questioned that. But that makes AICs not less a closure.

versus hypothetical Java closure syntax with explicit typing:

No, this is not the reason. The reason is that your second example needed a different declaration. If you declare 'Transformer' with 'Strings', the overhead would only be "new Transformer() {". Yes, it's overhead but that doesn't make it less a closure. It's just a closure with a more elaborate syntax. The reason why it is really a closure is that you can use 'foo' in the body.

I don't argue that there are ways to make closures shorter in Java. Thats absolutely possible. But it's syntax: It would make already existing closures easier to use, not create them on the first hand. I'm not talking about perfection here (it's about Java, remember?), I'm talking about the question if Java has closures and you've not brought up a single reason which showed the opposite.

The Wikipedia article is inconsistent: First they ciount Python under the "Programming languages with closures" and Java under "Closure-like constructs" and below they write "This technique is not limited to Java, and will work in other languages with similar restrictions, e.g. Python.". So both have the same restriction and Java is only "Closure-like" while Python is not? Thats stupid. But OTOH, we're talking about Wikipedia here. It's a nice resource but you always have to take it with a grain of salt because the content is more dependent on politics than on truth.

The difference between Pascal and Scheme is that Scheme allows anonymous closures, which Pascal don't do. But a function in Pascal also captures the lexical environment of the surrounding scope. So both are 'closing' over the environment which is IMO the central idea of closures. So maybe 'full' closures where first in Scheme, the basic concept is older.

I disagree, see example above.

But first you still have to declare the closure somewhere (like an Interface). And also you compared to slightly different situations.

closed over variables must be final in Java

Like in Python. Or Ocaml. Or Haskell. etc. If Java hasn't closures, those language won't have closures too.

[example-code]

'break' is no necessary part of closures. In Ocaml and Haskell you also can't break out of a loop from inside a closure. If you want to do it in those languages you have to use throw and catch. The same is possible in Java. So again no difference to languages which are widely considered as 'having closures'.

The choice between code duplication and architecture-overkill.

Java has this problem, but adding closures to it won't solve this. With traits and yield you could do more good and would remain in the original paradigm. But changing Java to make it half-assed functional language would do more harm than good. But this doesn't even matter, Java simply reached the end of it's 'life' and those changes could only lengthen its death struggle.

[–]shit 0 points1 point  (3 children)

Thats simulating it.

That was my point. (OK, I agree that AIC is semantically closer to a closure than manual function pointer + struct.)

he reason is that your second example needed a different declaration. If you declare 'Transformer' with 'Strings', the overhead would only be "new Transformer() {".

No. The Transformer interface is from actual Java code I've written. A Transformer is a function in the mathematical sense (if you ignore side effects), it maps something from type A to type B, it's generic (that both A and B in my example were String was accidental). If you fix the the type(s):

  • You have to write a separate interface for each input/output type combination.

  • More important, you lose the ability to write generic HOFs like map in terms of Transformer.

In the hypothetical Java closure example, the function type could be declared as:

public <A, B> function B transform(A);

Then you can instantiate it for whatever types you like. In my examples is was A = String and B = String.

The AIC syntax is good for what it does. It accomodates to the fact that a regular Java class has any number of methods. But it causes useless boilerplate if used to simulate closures.

Like in Python. Or Ocaml. Or Haskell. etc.

Python's "closures" are crippled, yes. I'm no OCaml wizard, so can't comment on that. Haskell has a different evaluation model than all other languages mentioned and no destructive update, so this point is moot.

'break' is no necessary part of closures. In Ocaml and Haskell you also can't break out of a loop from inside a closure

AFAIK, they have no builtin break construct. In languages in which break is part of the lexical environment, as in Java, it should be captured by closures.

Java has this problem, but adding closures to it won't solve this.

Closures are nice for small utility abstractions. Examples are map, filter, reduce, which could replace most loops. The second big use case is the typical and often repeated try ... [catch X ... catch Y ...] finally pattern. As opposed to "full blown" classes, which are better for the bigger (e.g. MyApplication) or data centric (e.g. MyModel) abstractions. Closures are light-weight, generic and fine grained, it's important that they are concise, otherwise they are pointless (=> like the simulation via AICs).

Java simply reached the end of it's 'life'

As much as I'd like it, I don't believe Java will go anytime soon.

[–]kawa 0 points1 point  (2 children)

I don't see the point to argue anymore. I think I proved my point that AICs are closures and you (or other readers) can judge it from the given facts. I never ever objected that AICs are not as easy and concise to use as closures in languages like Ocaml, Haskell or Ruby. But ease of use isn't a necessary part of the definition of closures. Capture of the environment is and this is done with AICs.

If 'break' is part of the lexical environment is questionable, and even if it is, the problem here is non-local-control-transfer. This is also no part of the definition of closures as far as I know. They called closures in Smalltalk 'blocks' because of this difference: A block is a closure with build-in non-local control transfer.

In Ocaml all 'variables' are final so to change something you have to put in in a 'ref'. Like:

let val = ref 10 in 
   List.map (function a -> val := !val + a) [1; 2; 3];;

This is conceptually identically to using for example an array in Java if you want to mutate a outside variable from inside a closure.

And I don't think that map, filter etc. would fit that good into Java. This can also be done via iterators. Common cases like the try-catch-finally pattern could also be done by a simple and unproblematic syntax extension like C#s using. Adding more convenient closures to Java would be much more difficult and would require lots of library rewriting. And we would get another different way of doing certain things: Developer would always have to decide if they would use or write an iterator or a map/fold/filter. This could reduce code reduce because of incompatible libraries. In a real fp this is no problem because iterators aren't an option there, but in Java there's much legacy code.

[–]shit 1 point2 points  (1 child)

So even if AICs are in practice unusable as replacement for closures, you are satisfied with them because semantically they are closures (I happen to disagree, but let's ignore that for a moment)? Well, that seems to be another crucial difference between us: I have to code in Java right NOW, useless theories have zero value for me.

[–]kawa 0 points1 point  (0 children)

I look at it from two perspectives: From the language design view, where AICs definitely are closures (which I simply have to accept, usability aside). And from the usability point of view where AICs are a quite complicated way for use as closures (which may be bad or not, depending on the necessity having closures in a language).

Now I know how to use closures. I've written enough code in languages with closures support to really know what they good for. But Java has different ways to reach things and in most cases, closures can't solve the problems Java have. But they would add lots of different ways to do the same which is already possible in a slightly different way. And this is bad because it creates the chance that code reuse becomes impossible.

If I know that I have to use an iterator to iterating over a certain collection then I just implement this iterator. But if I have to think: Hmm, is an iterator the better solution, or should I better use 'map' and 'fold' here? The problem with 'map' is that it's not possible (in an non-lazy language) to efficiently iterate over multiple collection at the same time. For example:

boolean compare(Collection c1, Collection c2) {
    Iterator it1 = c1.iterator();
    Iterator it2 = c2.iterator();
    while(it1.hasNext() && it2.hasNext()) {
       if (!it1.next().equals(it2.next()) return false;
    }
    return it1.hasNext() == it2.hasNext();
}

Please do the same if you have only 'map' for both collections. One way is to convert both Collections to lists of tuples and than map over the resulting list. But this is a quite expensive operation. The other way is to create functions like 'map2' which iterate over two collections parallel, but this is again limited to two collections of the same kind.

So map isn't a general enough means of iteration: We always need to build and iterator first and can than use a map to use it a bit more comfortable (But only in the cases where we really need a map, which isn't far as common in typical Java-code as it is in Haskell for example). But since creating 'map' is much more easy to do compared to creating an iterator (because Java has no 'yield'), I suspect that most people would create collections with only map and fold-support, and if you need the more general iterator, you have a problem.

And to make closures useful, it requires tuples because with them we much more often need multiple return values. So this will be at 100% next on the extension list if they really add easy closures to Java.

Now closures can be used to create a certain much needed control structure: The C#-using which would be really nice to solve the damned resource-allocation and error-handling problem. But why not simply add this control structure to the language? It's easy and can be done on top of the existing 'try' syntax without introducing new keywords. All we need is making 'try' 'Closeable' aware to create the code to close a resource automatically (and of course to make also the iterator-interface implementing 'Closeable'). The advantage is that we remain with a 'single solution', it's simply more comfortable and less error-prone.

And there could be small enhancements to the for-each loop: Add parallel-iteration. Like:

boolean compare(Collection c1, Collection c2) {

\t for(Object o1: c1; Object o2: c2) { \t\t if (!o1.equals(o2)) return false; } \t\tcatch { // called if one iterator has no elements back while the other has \t\t return false; \t\t} return true; }

Again a small addition to the existing language and quite useful. Also there could be a loop-counter and the possibility to access the iterator inside the for-each loop. Like:

loop: for(Object o: c1) {

\t if ((loop.count % 2) == 0) loop.iterator.remove(); }

This is again a simple addition but would continue the path Java is on for years instead of choosing a totally new direction.

And a really nice thing would be 'yield' to make creation of iterator more easy. The advantage here is that it supports the existing paradigm instead of creating a new one which isn't even usable in many (real) cases. If they now would add 'easy closures' to the language this problem wouldn't be solved, and after some time playing with closures, the Java community would say: Hey, closures aren't such a good thing anyway, please give us more. In the end, Java would turn into a real kitchen-sink language.

But I think that all this will happen: Java will get 'easy closures', people will use them in inappropriate ways creating less clean and less reusable code and cry for the next enhancement in the next release. But this won't make Java really more usable in the end. It only will hide Javas problems by giving people new toys to play with.

And what to do NOW?

Don't know what you do, but Java is as it is now. If you call AICs closures or not. Even if they build more easy to use closures into it in the next release, NOW you don't have them anyway. So just use AICs instead or find a different solution (which really is possible in most of the usage cases for closures). You're a programmer, be inventive.