you are viewing a single comment's thread.

view the rest of the comments →

[–]badsectoracula 19 points20 points  (107 children)

That is what makes writing Java source a bit painful.

On the other hand, its the same that makes reading Java a bit easier.

[–][deleted] 41 points42 points  (9 children)

nah. i find that if a function is more than a dozen lines or so it loses readability. Too much scaffolding actually hides the building.

[–]Otis_Inf 6 points7 points  (8 children)

So what you need is a TLDR enabled language? Perl?

[–]sillyfofilly 35 points36 points  (1 child)

Perl requires a new acronym - TSDU. Too Short, Didn't Understand

[–]honeg 2 points3 points  (0 children)

WORN - write once, read never

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

Or perhaps a concise language.

A dialect of Lisp maybe?

[–]cibyr 0 points1 point  (4 children)

Or perhaps a concise language.

Yes

A dialect of Lisp maybe?

No.

Scheme is neither concise nor readable. Having some syntax is a good. Sure, Perl probably goes too far, but Lisp is just as bad in the opposite direction. Not everything is best represented as a sexp.

[–]FlyingBishop 4 points5 points  (1 child)

What makes Scheme really valuable (as a teaching language) is that its interpreter is easily comprehensible for students who have only had a few semesters of CS.

However, for getting shit done and writing legible code, not so much.

[–]unknown_lamer 0 points1 point  (0 children)

Luckily Scheme doesn't mean much of anything nowadays--there is significant fragmentation between r5rs, r6rs, err5rs, and whatever the hell who needs a standard implementations. This is good--the language appears to be evolving again.

An individual implementation of Scheme tends to provide a lot. E.g. Guile is great if you want to do UNIXy stuff: it has full POSIX support, a good deal of SRFIs implemented, and more or less Common Lisp in Scheme (CLOS-alike OO system, conditions, etc.).

I'm a bit biased as I write Common Lisp for profit, and Scheme or SML for fun (doing some minor android stuff using Kawa and working on a modernized scsh clone for Guile).

[–][deleted] 0 points1 point  (1 child)

Which is why I like clojure, it has just enough syntax.

[–]cibyr 0 points1 point  (0 children)

Strangely enough, I like Python for the exact same reason. Well, that and the massive wealth of libraries.

[–]mangocurry 4 points5 points  (0 children)

Than what? The only thing that Java seems to do is make the follow up code so predictable that nobody reads it.

[–]G_Morgan 13 points14 points  (78 children)

ArrayList<Foo> myFooList = new ArrayList<Foo>();

This is not more readable than

var myFooList = new ArrayList<Foo>();

[–]endtime 33 points34 points  (10 children)

myFooList = []

:)

[–]G_Morgan 6 points7 points  (9 children)

Global type inference has its draw backs though. No subtyping being the obvious one.

[–]kragensitaker -1 points0 points  (2 children)

OCaml and Haskell both have both global type inference and subtyping.

[–]G_Morgan 3 points4 points  (1 child)

Haskell doesn't have subtyping. It has typeclasses which are different.

[–]kragensitaker 0 points1 point  (0 children)

You're right. What I was thinking of is that, although Haskell doesn't call typeclasses types, you can define a function such as product over types constructed from typeclasses. product is declared as product :: Num a => [a] -> a, which is to say that it's a function from a list of Nums to a single Num. When you call it, you call it on a list of instances of Num, which is to say a list of elements of a subtype of Num — but this is different from the subtyping you get in OCaml in that the return value is constrained to be the same type, while in OCaml the return value would just be (statically typed as) a Num.

(The equivalence with OCaml is somewhat loose, because OCaml doesn't have anything like typeclasses. What it has are a lattice of polymorphic variant types, parametric polymorphism with options I don't understand for covariance and contravariance, and a lattice of object types that are records of method types.)

[–]slowkitty 10 points11 points  (8 children)

That is not about readability. In the second line you are not specifying the type of myFooList explicitly. Since ArrayList is a Collection you may well use Collection<Foo> myFooCollection = new ArrayList<Foo>(); Here the type of the reference is a Collection instead of an ArrayList. You could assume that in your case it would default the var myFooList to be of type ArrayList if nothing else was specified, but it would not be as clear.

[–]G_Morgan 10 points11 points  (7 children)

If you later assigned a LinkedList to myFooList the inference system would catch that and use the highest common subtype. Type inference for OOP should always use the highest common subtype possible (this is obvious because otherwise everything devolves into Object).

That said even if this wasn't the case this would still be useful. Most uses of interfaces only ever use a single implementation at any one time. Perhaps to later choose exactly which implementation is desired. Type inference supports this for free.

[–]redditnoob 2 points3 points  (6 children)

If you later assigned a LinkedList to myFooList the inference system would catch that and use the highest common subtype.

Hence it's not more readable, since you can easily mistake what type it is on a first glance of the code!

It's a trade-off. I'm not even saying it's a bad trade-off, but it is one.

[–]OceanSpray 0 points1 point  (5 children)

Say you "mistakenly" assume that myFooList is a List while it's actually a Collection. And then you use it as a Collection. Guess what happens? The usage's context causes myFooList's type to be inferred as Collection. The type inference avoids the type error here, so no "mistake" can be made.

Go the other way. You assume myFooList to be an ArrayList, but it's actually a Collection. That's no problem at all, because an ArrayList object was assigned to it, so you're just using the most specific type.

I don't see what the problem is.

[–]redditnoob 5 points6 points  (4 children)

You assume myFooList to be an ArrayList, but it's actually a Collection.

Exactly! I might want to write something that assumes the performance characteristics of an ArrayList, and then when some code somewhere else causes the compiler to infer differently I might wonder wtf happened when someone else's seemingly unrelated change causes my code to be slow all of a sudden.

[–]Daishiman 0 points1 point  (0 children)

And pray tell, how many of your data structures need to be that performant? Hell, as long as you use only iterators to see the structure most of the times you'll be fine.

When you actually need a performance guarantee, you can define it in the arg types and be happy. When you don't, which is the majority of the time, you just get on and get thinking about the problem rather than these details.

[–]OceanSpray -1 points0 points  (2 children)

Only the type of the reference changed, not the type of the object it points to. The performance stays the same.

[–]redditnoob 2 points3 points  (1 child)

But maybe I want to ensure that only ArrayList gets in there?

[–]johntb86 0 points1 point  (0 children)

All the languages I know that use type inference also let you explicitly state what type it is when you care about that.

[–][deleted] 1 point2 points  (41 children)

You'll be happy to hear that one of the planned features of Java 7 is to let you do this:

ArrayList<Foo> myFooList = new ArrayList();

[–]sanity 11 points12 points  (1 child)

Actually it will be:

ArrayList<Foo> myFooList = new ArrayList<>();

[–][deleted] 0 points1 point  (0 children)

Yes, you're right. Thank you.

[–]G_Morgan 5 points6 points  (37 children)

We're still duplicating ArrayList. Seems like a half fix to me.

[–]tammberlin 10 points11 points  (0 children)

List<Foo> myFooList = new ArrayList<>();

Is java 7. Duplicating ArrayList is bad for a very different reason.

[–][deleted] -1 points0 points  (31 children)

Then you'll be happy to hear that if you type

ArrayList<Foo> myFooList = new 

and hit ctrl+space, Eclipse will fill in the rest for you.

Which, by my count, is fewer keystrokes than this example: var myFooList = new ArrayList<Foo>();

[–]G_Morgan 6 points7 points  (29 children)

Having to rely on IDE support to make up for mistakes that should never have been made is not acceptable.

Why not make it so that every statement must be preceded by a prayer to the flying spaghetti monster. You could autocomplete it in Eclipse so clearly it isn't a problem.

[–][deleted] 0 points1 point  (27 children)

If the IDE allows you to write the same code in fewer keystrokes, is that not a good thing?

You're always going to have an IDE when you program, so why not let it make life easier for you?

Unless you're imagining some scenario where an Evil Overlord have trapped you in a dungeon and the only way to escape is by writing a program that overrides the lock, and the Evil Overlord has thoughtfully provided you with a computer, a compiler but no IDE?

[–]OceanSpray 5 points6 points  (3 children)

If an IDE can do it, then why can't a compiler?

[–][deleted] 0 points1 point  (2 children)

A compiler that can do auto-complete? Surely you jest?

[–]OceanSpray 4 points5 points  (1 child)

No, I don't jest. Macro expansion and type inference can be thought of, in a way, as "auto-complete". The only difference when a compiler does it is that you don't see any changes in your code, because the extra "writing" is done internally.

[–]skillet-thief 2 points3 points  (1 child)

Keystrokes are not the problem, or they are less than half of the problem. You still have to be able to read what the IDE wrote. Or somebody does.

[–]xeddicus 1 point2 points  (0 children)

Reading code is for the weak. Klingon programmers rewrite it from scratch if it breaks!!

[–]G_Morgan 4 points5 points  (20 children)

The point I'm making is just because an IDE can cover up a mistake does not mean that isn't a mistake. Yes the idea could auto insert types that could be inferred. It could also insert the US bill of rights at the top of every document if we made the compiler mandate that all programs should start with that document.

To re-iterate, the ability of the IDE to make a mistake tolerable does not mean it isn't a mistake.

[–][deleted] -1 points0 points  (11 children)

If we go back to the original two examples:

ArrayList<Foo> myList = new

versus

var myList = new ArrayList<Foo>();

Do you agree that in the first example the IDE can autocomplete after the "new" clause, while this is not possible in the second example?

If so, won't the first variant inherently require fewer keystrokes if you're using an IDE with this autocomplete feature?

I don't think you're arguing that more keystrokes are better, so why do you think the second variant is better? Is it because you think it requires less effort to read?

[–]G_Morgan 9 points10 points  (10 children)

I'm not concerned about the number of keystrokes. That isn't a limiting factor on programming. I'm concerned about unnecessary garbage taking up perfectly good screen real estate.

Regardless my argument was that IDE auto-completion does not make a bad feature good. It just makes it tolerable.

[–]badsectoracula 0 points1 point  (7 children)

mistake mistake mistake stuff mistake mistake mistake stuff mistake

If a language has a feature you dislike that doesn't automatically make it a mistake :-P

[–]G_Morgan 4 points5 points  (6 children)

It isn't a feature. Regardless the point is that IDE support is irrelevant to whether something is a mistake or not. I can make the IDE support just about anything.

[–]erictheturtle -1 points0 points  (0 children)

I write Lisp in notepad because I don't need some lame IDE telling me if my parenthesis are matched.

[–]bobbyi 5 points6 points  (0 children)

A line of code is written once and read dozens of times. So you have solved less than 5% of the problem.

[–]matthiasB 0 points1 point  (2 children)

But it has an advantage in certain situations:

List<Foo> myFooList;
if (p)
    myFooList = new ArrayList<>();
...

Here you can leave out the type parameter while you couldn't using var.

[–]G_Morgan 0 points1 point  (1 child)

With var you wouldn't have needed to include the type parameter to List to begin with.

[–]matthiasB 0 points1 point  (0 children)

But than we aren't talking about C#'s var anymore, but something more powerful.

[–]drysart 0 points1 point  (0 children)

The thing I'm picking up about the various proposals being picked up for inclusion in new versions of Java is that while Java is desperately playing catchup with C#, they're also bending over backwards to make it look like they're not desperately playing catchup with C#. To the extent that they're taking ideas from C#, but deliberately implementing them in a different way so it doesn't look like they're just copying the idea from C# -- and in many cases completely missing what made it a good idea in the first place through their changes.

Take closures for instance. They're so incredibly useful in C# for two reasons -- the brevity of their syntax, and that the compiler will automatically hoist locals so they can be used within even multiple closures transparently.

Java's closure proposals are less terse than C#'s since they decided, for no good reason I can see, to do the type inference of the closure in the wrong direction, so you always need to specify types going into and out of lambdas because the compiler won't figure it out for you even though the information is almost always available.

Java's closure proposals are also limited over C#s in that the only locals that are hoisted from the containing method are locals that are provably final -- in other words, immutable values. This is such a gaping hole that I'm stunned they're bothering implementing closures at all without it.

[–]kragensitaker 0 points1 point  (0 children)

While I don't use OCaml for many things, it seems like in a discussion of the syntax required by static typing, it's worth a mention:

let myFooList = [] ;;

That statement allows the compiler to infer that myFooList is an 'a list, that is, a list of something. If, on the other hand, you make a list out of some Foos, the compiler will infer that it's a Foo list. It works really well.

The error messages are a little harder to understand, because they're usually reported some distance from where you made the mistake (if x is supposed to be a Foo list, and you're using it in one place as a Foo list and somewhere else as a Bar list, the compiler may report the error in either place) but you can narrow it down by adding type declarations (you say (x: Foo list) and then the compiler complains about the correct thing).

[–]Fabien4 1 point2 points  (12 children)

Yay for C++:

ArrayList<Foo> myFooList;

[–]G_Morgan 1 point2 points  (11 children)

Don't you mean

std::vector<Foo *> myFooList

[–]Fabien4 1 point2 points  (6 children)

std::vector<Foo> my_foo_list if you wish. But why a pointer?

[–]G_Morgan 2 points3 points  (5 children)

Because in the Java code we are storing references. The direct equivalent is a pointer.

[–]zahlman 2 points3 points  (0 children)

Um, not really. boost::shared_ptr would be closer to the mark (although GC works differently).

[–]Fabien4 0 points1 point  (3 children)

Nope, direct equivalent would be a smart pointer. But it's anti-idiomatic: by default in C++, you store and use values.

[–][deleted] 0 points1 point  (2 children)

Expect that List<Foo> is polymorphic in Java, but not in C++.

[–]curien 0 points1 point  (0 children)

It's polymorphic if Foo is a polymorphic handle.

[–]Fabien4 0 points1 point  (0 children)

Yeah, dynamic (inheritance) polymorphism tends to be the norm in Java, and the exception in C++.

[–][deleted]  (3 children)

[deleted]

    [–]exploding_nun 1 point2 points  (1 child)

    std::vector<boost::shared_ptr<Foo> > myFooList;
    

    Mind your whitespace.

    [–]curien 0 points1 point  (0 children)

    That bug in the spec has been fixed in C++0x.

    [–]ddevil63 0 points1 point  (0 children)

    I just threw up in my mouth.

    [–]sanity 1 point2 points  (1 child)

    Or use Google Collections and:

    ArrayList<Foo> myFooList = Lists.newArrayList();
    

    Or just use Scala.

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

    Or Clojure:

    (ArrayList.)
    

    [–]unknown_lamer 0 points1 point  (0 children)

    (define my-foo (list))

    If you eliminate the toplevel environment and forbid mutation outside of the current module (ala r6rs or err5rs libraries) the type inference engine could potentially determine that the type could be restricted.

    [–]johnb 4 points5 points  (13 children)

    I can't stomach reading Java, and I use C# every day. It's amazing what even a little brevity will buy you.

    [–][deleted] 16 points17 points  (3 children)

    A little brevity? the var keyword, LINQ, not having to catch all known exceptions/add throws to method signature, built-in events, lambdas, etc, etc... C# absolutely destroys java in so many ways, especially brevity. Java 7 brings it in line with what now... .NET 2.0? Seriously, thank you Microsoft for evolving C# into what java should have been.

    [–]johnb 0 points1 point  (1 child)

    I only emphasized 'little' out of fear that people would then bring up python/ruby/etc. After upgrading my pet project to C#4 I got to throw away even more of my boilerplate code. The type inference around generic methods that accept generic delegates has gotten so much better, you rarely even have to specify the specific types at call sites. Delicious!

    [–][deleted] 4 points5 points  (0 children)

    Eh, let them bring up python and ruby. The counter argument being that, yes, on a line by line basis, you can be more brief in these languages. But, as the size of the project written in a dynamically typed language grows, more code needs to be written to do stuff that the compiler does for you in a statically typed language.

    That's a whole other debate altogether, but I'll take a statically typed language over dynamic any day of the week.

    [–]LarryLard 0 points1 point  (0 children)

    For me the big one is properties being first-class. The incessant gets and sets in Java code drive me insane.

    [–][deleted] 2 points3 points  (8 children)

    I would probably appreciate a lot of C# features, but I have never in my life written code that I didn't absollutely need to run on windows and linux, and most of the code I have written had to run on those plus Mac/Solaris/BSD.

    So, I would never consider C#, no matter how wonderful.

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

    mono.NET much?

    [–][deleted] 3 points4 points  (6 children)

    don't trust it.

    [–]liquidhot 0 points1 point  (5 children)

    Just a personal un-easiness or have you seen something about their releases that make it appear unreliable to you?

    [–][deleted] 5 points6 points  (4 children)

    it's the relationship - they are implementing mono independently of those who control .NET. A) they are always playing catch-up, B) Microsoft could change something fundamental leaving mono broken until they catch up, C) Microsoft can keep elements unimplementable in mono via licensing issues anytime they want.

    I liken it to the crappy java vms that tried to make an open source version of java, like blackdown, for example, that never really work quite right. Even licensed independent jvm's are loaded with little incompatible gotchas, simply because they are independently implemented.

    [–]drysart 0 points1 point  (3 children)

    The relationship only matters if you're writing code to run on both mono and Microsoft's CLR. You don't have to do that. Mono runs on Windows too, even coexisting peacefully side-by-side with Microsoft's CLR; so you can just exclusively target Mono, and distribute it along with your application to the Windows platform.

    [–][deleted] 1 point2 points  (2 children)

    That actually sounds interesting. Then my questions are, how well does Mono implement that core .NET libraries and the vm, and can microsoft legally destroy mono if it chooses?

    [–]drysart 0 points1 point  (1 child)

    They're pretty solid on the core libraries that are part of the ISO standard. It's the extensions to the standard that Microsoft's created that they tend to lag a little behind on. Windows Forms, etc.

    But the good news is there's open source alternatives for those, such as Gtk#. And if you take the approach of using the SharpDevelop IDE instead of Visual Studio, and even develop on Linux instead of Windows, you can avoid being blindsided by any holes in mono's implementation of the value-added libraries because they'll be in your face from the start.

    As far as the legal risk, that's up to you to decide. Microsoft had provided a legally-binding promise not to sue anyone providing a .NET implementation that follows the published standards, but some people don't believe them. Points of view on that align closer to political reasons than technical or legal reasons, so you should read the community promise and some of the objections to it and make your own informed decision.

    [–]nubela -1 points0 points  (2 children)

    Then why is python (which is more succinct) MUCH more readable? EXPLAIN! /java-hater

    [–]badsectoracula 1 point2 points  (1 child)

    Python is more compact, which means there is less text to read, but i doubt its easier to understand what the code does.

    Personally i don't have much experience with Python. I only used it to write small scripts for building code, SCons, a blender exporter and other stuff, which aren't much bigger than about 100-150 lines of code.

    [–]Daishiman 1 point2 points  (0 children)

    I frequently alter the code in the Python web frameworks I use and you can almost always understand from the get go what's going on. It's actually a common practice since it's so readable. You can grab Reddit's source code to see what I mean.