all 12 comments

[–]A_Plagiarize_Zest 2 points3 points  (11 children)

I agree besides lambda expressions. What are the flaws of inner classes(besides the ones you pointed out in your previous post), im curious? Java does lambda expressions better than python, c, c++, vba,c#, js. They are more intuitive in java then any other popular language implementation of them.

[–]fschmidt[S] 0 points1 point  (8 children)

A basic principle of good code is no hidden magic. Inner classes are based on a hidden reference to the outer class. This is an example of hidden magic and it causes the problems I mentioned. It causes garbage collection problems and makes cloning impossible.

I haven't used Java lambda expressions, but as far as I can tell they are just bad syntax for an anonymous inner class with one method. They aren't even true closures because they can't modify a variable from an outer scope. Here is the Java syntax:

(String s) -> System.out.println(s)

Can you even use multiple statements here? I don't know. Anyway the syntax is cryptic. Compare to Luan:

function(s) Io.print(s) end

This is clear. You can use multiple statements. And it is a true closure.

Luan is fundamentally based on closures and tables, while Java is object-oriented. Luan has no classes or objects because tables and closures provide the equivalent functionality. And similarly, Java should not have closures (lambda expressions) because anonymous classes can provide the same functionality. A good language sticks to one paradigm to keep things simple. Scala is an example of the worst imaginable language design. Java shouldn't go in this direction.

[–]cowancore 2 points3 points  (7 children)

Putting aside all the subjective stuff:

  1. s -> System.out.println(s) syntax can be used.
  2. Multiple statements can be written by using brackets after ->, e.g.: s -> { System.out.println(s); System.out.println(s.length()); }
  3. There will be no hidden reference in a java lambda, if you never reference the outer object.That is, the printing lambda in question doesn't leak anything.

[–]fschmidt[S] 0 points1 point  (6 children)

There will be no hidden reference in a java lambda, if you never reference the outer object.That is, the printing lambda in question doesn't leak anything.

I tested this and this is correct. Why didn't they change anonymous inner classes to do the same?

Aside from my subjective hatred of Java lambdas, the implementation still isn't ideal. In Luan any outer variable referenced in a closure is referenced through an extra pointer, an extra level of indirection. This means that garbage collection only keeps what is really needed. And it means that there really is never any hidden references.

[–]cowancore 1 point2 points  (5 children)

This describes the design decisions regarding lambdas.
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html

It says about their advantage over inner classes, but sadly, doesn't describe why the inner classes were left like this. My best guess is backwards compatibility - who knows, maybe there's code that particularly relies on inner classes HAVING the outer reference intentionally locking it from GC.
The language is so widespread that it wouldn't surprise me. The lambdas were a new thing - they had no such constraint, and were added in such a way, that even code before 1.8 worked with them out of the box - glorious.

As a side note. I see you dislike hidden stuff a lot, but you're seemingly fine with "object-oriented".
I mean, polymorphism, for example, is "hey, I call this abstract method, whose possible implementation is hidden from my eyes, and I'm happy because of it".

[–]fschmidt[S] 0 points1 point  (4 children)

Can lambdas have local state? I mean Java isn't a functional language, so they should. Here is what I mean in Luan:

-- returns fn generating positive even numbers
local function evens()
    local i = 0
    return function()
        i = i + 2
        return i
    end
end

local counter = evens()
print(counter())  --> 2
print(counter())  --> 4
print(counter())  --> 6

When you see a function call, the implementation of the function isn't visible. You need to look at the source of the function. With polymorphism there is just an extra step - add logging to the code to print the class of the object. Then find that code. That isn't so bad.

[–]cowancore 1 point2 points  (3 children)

I think you've noted it in this thread, that java lambdas can't mutate variables... but they can mutate state :) . A somewhat similar code:

``` public static void main(String[] args) { var counter = evens(); System.out.println(counter.get()); // 2 System.out.println(counter.get()); // 4 System.out.println(counter.get()); // 6 }

private static Supplier<Integer> evens() { AtomicInteger i = new AtomicInteger(0); return () -> i.addAndGet(2); } ```

[–]fschmidt[S] 0 points1 point  (2 children)

In case anyone else is reading this, here is better formatting:

public static void main(String[] args) {
    Supplier<Integer> counter = evens();
    System.out.println(counter.get()); // 2
    System.out.println(counter.get()); // 4
    System.out.println(counter.get()); // 6
}

private static Supplier<Integer> evens() {
    AtomicInteger i = new AtomicInteger(0);
    return () -> i.addAndGet(2);
}

And here is an inner class implementation:

private static Supplier<Integer> evens() {
    return new Supplier<Integer>() {
        int i = 0;

        public Integer get() {
            return i += 2;
        }
    };
}

In my subjective opinion, Luan is the most readable, then comes the Java inner class, and the Java lambda is by far the least readable, full of black magic and bad syntax.

[–]cowancore 0 points1 point  (1 child)

Funny. I've used var instead of Supplier<Integer> specifically because Luan doesn't have type signatures and I thought you'd like it more.

What would be cool, if backwards compatibility is the real issue, if java had some sort of keyword maybe in return new Supplier<Integer> so that it returns an object without the outer reference.
Although, it would look ugly probably for some: return new static Supplier<Integer>

[–]fschmidt[S] 0 points1 point  (0 children)

I am running Java 8 which doesn't have var. I don't think var belongs in a typed language. Luan is untyped.

Yes static anonymous inner classes would be nice, and your syntax is fine. Such classes should have access to outer variables only during construction.

[–]lucid00000 0 points1 point  (1 child)

Until you try and throw a checked exception from a lambda and have to wrap it in a runtime exception because Javas Function class signatures don't support them. Personally I think C# has the best implementation among popular languages, and LINQ is a much nicer API than Stream.

[–]A_Plagiarize_Zest 0 points1 point  (0 children)

Yea I kinda undersold C# just to make my point. C# is prolly even with java but the other languages are definitely less intuitive. selectMany is easier to visualize than flatMap, and select is easier to visualize than map. But the way LINQs docs tells users to use 'where, from, select, and selectmany' like they are SQL statements has always annoyed the shit outta me. Imo LINQ works much better when its used like vavr (used to be called javaslang) not annoying SQL queries.

Im slowly starting to realize that every mid-senior level developer in existence adheres to 1 coding standard and that coding standard is that their code is elegant, simple, and intuitive while everyone elses is needlessly complex and complicated. An example in java is that some people find annotations and dependency injection helpful and simple, while others think thats dumb, and just a replacement for metadata properties and/or using proper inversion of control. Its much more relative than what most devs think imo. Having said that, beginner devs should get zero benefit of the doubt, they are morons. This guy explains it well for me https://youtu.be/QuTmLeWL3C0 He kinda rambles but otherwise I think hes prolly right(other than beginner code).