Hacker News new | ask | show | jobs
by lemevi 3859 days ago
How is Java low level. Java is a high level language. What is "low level" about Java? Do you mean statically typed makes it low level? No it doesn't. It makes it safer though. Low level means direct access to hardware and having to deal with network protocols manually and shit like that. None of which you almost ever do or would want to do with Java considering it runs on a cross platform runtime mostly isolated from the actual OS of the host machine.
3 comments

It means that when it comes to writing code to deal with a query language, which involves having an AST and manipulating it every which way, Java gets creamed by many other languages. Both the Lisp series of languages (which Clojure is in) and the ML series of languages have wildly better stories for dealing with AST manipulation, as in, better enough than Java that it's worth learning those languages just for that, if that's the task you have.

Whether or not Java is a "bad" choice is a definition issue, that it is handily beaten by many other languages is pretty concrete.

I'm not saying this as a Clojure partisan, or a Java-hater, or anything like that. I'm saying it as one who has used enough of the relevant languages to know it's not even close.

For something more concrete, I'm more on the ML side, so work through https://en.wikibooks.org/wiki/Write_Yourself_a_Scheme_in_48_... and you'll see what I mean, even if you just do the first couple of chapters. For all that Haskell may be weird in some ways, consider what it means for a tutorial of a language to be talking about parsing in the second section. You'd never get that with Java.

Java has a series of control mechanisms that originate from idiomatic assembly. for, while, if, variables.

In clojure for example you rarely use these low level constructs.

What is simpler to read? Which one is more high-/low-level

  List<Integer> even = new List()
  for(int i = 0; i < 100; i++){
    if((i%2)==0){
      even.add(i);
    }
  }
  return even;
or

  (filter even? (range 0 100))
The GP qualified that this is better with newer JDK versions. For instance, in Java 8 you can do:

    IntStream.range(0, 100).filter(i -> i % 2 == 0);
I find i -> i % 2 == 0), still less readable than even?.

But let's step up the game a bit. Let's produce a sequence of the form [true false false true true true false false false false ...] of exact length 1000.

  (take 1000 (mapcat (fn [i] (repeat i (odd? i))) (range)))
Using the Java 8 'stream' API:

   IntStream.iterate(0,  i -> i + 1)
	.mapToObj( i -> Collections.nCopies(i, i % 2 == 0) )
	.flatMap(Collection::stream)
	.limit(1000)
Generate an infinite series of incrementing numbers, starting from 0, map each to a stream of boolean indicating true/false, flatten that stream of streams, then limit the output.
Nicely done :D! You're the first one to get it right.

Yeah, I'd still argue that the clojure stream api is more readable though ;).

  (->> (range)
       (map #(repeat % (odd? %)))
       (apply concat)
       (take 1000)
The thing is that these additions slowly creep into java, which is great. But you still have to wait for the mercy of the language designers.

Clojure is so flexible that it is possible to bring these things in as libraries, resulting in a very lightweight and stable core, and a very rapidly evolving ecosystem.

Also the persistent data-structures of Clojure make life so much easier. It's basically a language build around persistent maps and arrays.

> Also the persistent data-structures of Clojure make life so much easier. It's basically a language build around persistent maps and arrays.

When programming in Java (which I did for 14 years), I always thought about the little machines I was making and how they interacted. In Clojure, I'm thinking about what shape the data should be and what stack of stencils and folds (as a visual metaphor) I need to get it into that shape.

That is to say, Java is object-oriented, Clojure is data-oriented. So take data-orientation and add easy-mode concurrency and you have something wonderful.

To close standard libs gap, let's assume I have static functions for odd() and take() as well as Java8 stream APIs statically imported. After this, it looks pretty compact and readable:

    take(10, i -> range(0, i)
                    .mapToObj(j -> odd(i))
                    .collect(joining(" ")))
    .collect(joining(" "));
For reference, odd and take will look like this:

    private static String odd(int i) {
        return Boolean.toString(i % 2 != 0);
    }

    private static <T> Stream<T> take(int n, Function<Integer, T> f) {
        return Stream.iterate(1, i -> ++i).map(f).limit(n);
    }
To paraphrase Hickey, "I find German less readable than English; that doesn't mean it's unreadable."
How about this then?

    IntStream.range(0, 100).filter(::even);
Not quite what was asked. The intended outcome is a sequence of length 1000 where the elements are 1 * true, 2 * false, 3 * true, 4 * false and so on.

To compare it with what your code produces.

[true false false true true true false false false false ...]

[true false true false true false true false true false ...]

How much assembly have you done? "for"/"while"/etc are coming out of structured programming.
Yes, but that in turn was inspired by the patterns and idioms in assembly that didn't fuck up your codebase.

Simple conditional forward jumps, if, simple conditional backward jumps, while, and simple conditional backward jumps depending on a variable, for.

Imperative != low level.
Because of the way our current hardware is build, it is actually Imperative <=> low level (ignoring forth for a bit).

Because impressive constructs have a somewhat straightforward compilation path to assembly. Even when GC if involved.

I think he meant in the sense of low-level because of the language primitives offered versus what is possible with clojure (which lets you define your own essentially).

Edit: I see others have offered more thorough answer while I was reading sorry for the redundancy ;p