263 comments

[ 4.4 ms ] story [ 295 ms ] thread
The matching syntax looks really good to me:

    // Java 21
    if (obj instanceof Point(int x, int y)) {
        System.out.println(x+y);
    }

    // Older Java
    if (obj instanceof Point p) {
        int x = p.x();
        int y = p.y();
        System.out.println(x+y);
    }
Its not substantial improvement, but another invariant which need to be learned and adds more brain load.

I hope someone will design some jvm SimpleLang eventually, which will have very few syntactic constructions but cover all/most of use cases.

Isn't that Clojure?

(let [p obj]

  (if (instance? Point p)

    (let [x (.x p)

          y (.y p)]

      (println (+ x y))))
I think Clojure is not statically typed? which is a big disadvantage, and also syntax is very different from current mainstream languages, so even more effort to learn is required.
When type checking is needed, I find the Truss library* does the trick quite well.

As for the syntax, there is very little, which can make it a harder lift but once you have the hang of it you won't deal with the issues identified in the parent comment.

* https://github.com/taoensso/truss

I don't know what Truss is, but I know that I inherited a few smaller Clojure projects / services, and it's untyped hell. Having optional typing means most people are not going to use it.
JS and Python are not statically typed, or PHP for that matter, but it hasn't stopped JS and Python topping the popularity list year on year nor did PHP stop Facebook from world domination.
I think in all cases it was realized that static types are adding significant benefits, so they were added to python, typescript and hack have been developed.
JS and Python have gradual, not static typing. Same for PHP 8 but Hack is a different language.
> JS and Python have gradual, not static typing.

that's because of all legacy dynamicaly typed codebases

For values of "legacy" referring to "new code written this afternoon, and tomorrow morning".
(comment deleted)
Clojure to the rescue ... again. Honestly, I can't fathom why Clojure isn't up there with the most popular languages. Paul Graham was right when he described it, well Lisp, as a secret weapon.
i would imagine the idiomatic clojure to do this is to convert the `p` into a bean using `clojure.core/bean`

but if it is a java record class, then there's currently no standard library way, since it's not a bean (getter/setter naming of properties). You'd have to write custom code for it, which sucks.

TXR Lisp:

Using quasiquote-style pattern:

   (if-match ^#S(point x ,x y ,y) obj
     (prinl (+ x y))
Or using @(struct ...) pattern operator:

   (if-match @(struct point x @x y @y) obj
     (prinl (+ x y))
All done with macros. Expansion, compilation, disassembly:

  4> (flow '(if-match @(struct point x @x y @y) (new (point 1 2))
              (prinl (+ x y)))
       expand
       prinl
       compile-toplevel
       disassemble)
  (let ((#:g0062 (struct-from-args 'point 1 2)))
    (let* (#:result-0065
           x y)
      (if (if (subtypep (typeof #:g0062)
                        'point)
            (let ((#:x0063 (slot #:g0062 'x))
                  (#:y0064 (slot #:g0062 'y)))
              (sys:setq x #:x0063)
              (sys:setq y #:y0064)
              (sys:setq #:result-0065
                (prinl (+ x y)))
              t))
        #:result-0065
        ())))
  data:
      0: point
      1: 1
      2: 2
      3: x
      4: y
  syms:
      0: struct-from-args
      1: subtypep
      2: typeof
      3: slot
      4: prinl
      5: sys:b+
  code:
      0: 2003000E gcall t14 0 d0 d1 d2
      1: 04000000
      2: 04020401
      3: 20010005 gcall t5 2 t14
      4: 000E0002
      5: 20020002 gcall t2 1 t5 d0
      6: 00050001
      7: 00000400
      8: 38000016 if t2 22
      9: 00000002
     10: 2002000A gcall t10 3 t14 d3
     11: 000E0003
     12: 00000403
     13: 20020009 gcall t9 3 t14 d4
     14: 000E0003
     15: 00000404
     16: 20020006 gcall t6 5 t10 t9
     17: 000A0005
     18: 00000009
     19: 2001000D gcall t13 4 t6
     20: 00060004
     21: 1000000D end t13
     22: 10000000 end nil
  instruction count:
     10
  #<sys:vm-desc: 9966330>
I see this is doing something silly: (subtypep (typeof x) y) is just (typep x y), but costs an extra gcall instruction.

Either the pattern matcher should do this, or the compiler should have that as an algebraic reduction, or both.

Let's do the compiler for fun:

  $ git diff stdlib
  diff --git a/stdlib/compiler.tl b/stdlib/compiler.tl
  index 58685741..a837571e 100644
  --- a/stdlib/compiler.tl
  +++ b/stdlib/compiler.tl
  @@ -1411,6 +1411,8 @@ (defmeth compiler comp-fun-form (me oreg env form)
              (set form (rlcp ^(,bin ,a ,b) form)))
             ((- @a)
              (set form (rlcp ^(neg ,a) form)))
  +          ((subtypep (typeof @a) @b)
  +           (set form (rlcp ^(typep ,a ,b) form)))
             ((@(or ignore nilf) . @args)
              (if (eql sym 'ignore)
                (each ((a args))
Note that we are using pattern matching here to recognize and rewrite this case. In return we will obtain better code from pattern matching:

  $ make
  TXR stdlib/compiler.tl -> stdlib/compiler.tlo
  $ ./txr
  This is the TXR Lisp interactive listener of TXR 291.
  Quit with :quit or Ctrl-D on an empty line. Ctrl-X ? for cheatsheet.
  TXR is not a toy, but should be kept within easy reach of children.
  1> (flow '(if-match @(struct point x @x y @y) (new (point 1 2))
              (prinl (+ x y)))
       expand
       prinl
       compile-toplevel
       disassemble)
  (let ((#:g0024 (struct-from-args 'point 1 2)))
    (let* (#:result-0028
           x y)
      (if (if (subtypep (typeof #:g0024)  ;; this hasn't changed, of course
                        'point)
          (let ((#:x0026 (slot #:g0024 'x))
                (#:y0027 (slot #:g0024 'y)))
            (sys:setq x #:x0026)
            (sys:setq y #:y0027)
            (sys:setq #:result-0028
              (prinl (+ x y)))
              t))
        #:result-0028
        ())))
  ** expr-1:1: warning: if-match: no such struct type: point
  ** expr-1:1: warning: new: point does not name a struct type
  data:
      0: point
      1: 1
      2: 2
      3: x
      4: y
  syms:
      0: struct-from-args
      1: typep              ;;; no mention o...
Oh yes, without the .stream()….collect(toList()) cruft! Just lists with .map() on them!

Also without the :: operator, it’s uglier than the arrow equivalent and it’s yet-another-construct to parse.

I disagree. One of the major critiques of Java is how verbose and full of boilerplate it is. And sure, you can hand wave that as devs being picky, but this is a real reason people avoid picking up new projects in Java. The verbosity serves no purpose, and less code is generally better.

As for there now being multiple ways to write the same thing, this seems like something that could be easily linted into consistency. So you would really only have to learn the less verbose way for any newer Java projects.

> of Java is how verbose and full of boilerplate it is.

yes, new SimpleLang can have lessons learned during last 20 years implemented, as in example from another commenter: https://news.ycombinator.com/item?id=37068380

Currently, for a given task, Java usually have 5 legacy deprecated ways to solve, and two more in newish Java versions, and dev needs to keep all of them in memory to be able to read and understand other's code.

> As for there now being multiple ways to write the same thing, this seems like something that could be easily linted into consistency.

that's if you don't work with any legacy systems and 3p libraries and never switch projects/jobs

Sounds like Kotlin is exactly such SimpleLang.
Ya, I'm not sure why people would use Java anymore given that the SimpleLang is pretty good.
Kotlin is even worse; it's younger than Java but also has 5 legacy ways to solve the problem and two more in the newer version, it's just that the legacy ways have been removed so any examples you find online won't work any more.
Are there any examples for this?

Kotlin sure moves faster than Java, but except for features that were experimental or unstable to begin with (coroutines, -X flags), I can't think of a case where more than 1 deprecated version exists (and the "deprecated" versions are not nearly as crufty as Java).

Those "experimental or unstable" features tend to be the advertised selling points of Kotlin. Particularly for async, the previous "experimental" APIs were the only ones available at the time and the recommended way of using Kotlin.
Kotlin is anything but simple
You way over exaggerate the number of different ways a given thing can practically be done in Java. Pattern matching for example only has the visitor pattern as a counterpart and you ain’t going around spewing visitor patterns, it not as simple.

Sure there are a few warts, e.g. arrays work a bit differently than Lists, and primitive-object discrepancy, but every language will pick up similar issues over time.

My comparison with c++ shows java is not more verbose if you take into account headers.
I think people are very aware that c++ is a lot more verbose than Java. What's the advantage of making this particular comparison?
I actually think c++ is not necessary more verbose than java as a language.

But memory management/safety, weaker IDE support and ecosystem make it less productive than Java for business dev.

Wait until you see this new, hyped language called Go! That is even more verbose than both!
I actually appreciate the simplicity of bare Java. It can be verbose on one hand but on the other there are no surprises because everything is just a method call on an object with no operator overloading etc. Seems like they're kind of ruining that with bizarre things like "obj instanceof Point(int x, int y))". That doesn't connotate at all that it's calling x() on the point to get x, it looks like a field. Compare with C# which has a ton of convenience features and syntactic sugar but it's all intuitive and self-explanatory. Really I wish Java would go away and .NET would take over but it's too entrenched.
Point is a record type here. Record types seem to be like Scala's case classes.

The distinction between fields and getters is intentionally blurred because these are immutable value classes.

Yes. Any language which requires an IDE to append get, set and toString for every damned variable is a language in trouble. Here we are at Java 21 and Java must be the only language in modern use which requires regex metacharacters to be escaped. This even after the introduction of raw string literals.
You missed out on Java’s records that solve your gripe with get/set.
Records are not the same as classes.
They are not, but if you need strictly a class for strictly storing data, records are superior in every way. Unfortunately some tools still want POJOs, but most (eg. Converting to json) do work with records.
Disagree with that, but please provide a counterpoint. I've using Java 17 for a large project and I was really excited to use records. I have not found any practical use case for them outside of one or two instances in a project with 2 years of development.

Storing data is also where I thought records would be a good use case. But in the real world data is not Point(x,y). If you have more than 3 or 4 fields in your record, that's simply not going to work. I thought the rest of the industry decided over a decade ago that using positional properties (e.g. new MyRecord(a,b,c,d,e,f,g)) to initialize an object was a bad idea. I think Rich Hickey gave a whole talk on that subject. If there's a future plan to be able to create records using named props, e.g. new MyRecord([firstName: "Bob"]), then I would reconsider my opinion.

Someone (I think pg?) said that patterns are just insufficient language features, but the flip side applies too: you can apply a pattern in any language, but a feature is limited to what the language designers provide. Patterns you can learn once and write anywhere (for better or worse).
Sounds like Kotlin to me
in my non-expert opinion, kotlin also added bunch of creative, unobvious and opinionated constructs to not be qualified as "simple lang"
Their implementation of closures is ass-backwards. Just a bare pair of braces with no arrow to disambiguate from block parens. Kotlin also added to Java's verbosity with extra modifiers like 'internal', 'open' and 'by lazy' which isn't even grammatical English. And don't get me started on multiple constructors.
Destructuring (/pattern matching) itself is supported by several languages (although more in general, its flexibility depends on the language). It makes sense to support it in modern languages, as it's practical and intuitive.

IMO it's a significant syntactical improvement, as it considerably reduces boilerplate assignments.

> It makes sense to support it in modern languages

the problem is that Java not modern language, but carrying all legacy features from last 20 years, so having all legacy features + new modern creative ideas make Java very complex.

What are some examples of these legacy features?

Overwhelmingly, I see complaints that java has been too simple compared to virtually all other languages. I agree that cognitive load is important. However, it feels really strange to me to suggest that java has reached the complexity of other similar languages. What am I missing?

for example Java went through several generations of IO evolution, so some simple task like reading text file line by line can be done in many different way in current standard library:

- using DataInputStream

- using Scanner

- using Reader

- using Files.readLines

and they all suck.

I see. Is it fair to say that most of the issues area related to jdk included libraries vs language features itself? I also find a lot of the included libraries to be cumbersome, but I see these syntax improvements to be a welcome change in contrast.
> Is it fair to say that most of the issues area related to jdk included libraries vs language features itself?

I think they all connected, for example with added lambdas they also added streams, so collections library now allows to do the same thing using old iterators or new streams APIs, and somehow those APIs are not compatible, it is not easy to produce stream from iterator.

Iterator is a very narrow API that needs to remain compatible with a lot of third party code, but streams rely on spliterators for concurrency. You can sort of get a stream from any collection (or a spliterator from any iterable and make a stream), but it will probably do all work sequentially.
> but streams rely on spliterators for concurrency

which I never seen anyone uses and it makes everything more complicated

they probably should've have some ParallelStream interface

That’s not language, but ecosystem. And I much prefer this to randomly breaking previously working code.
its standard library.

core ecosystem, e.g. apache commons will add several more ways.

IO has so many usecases that frankly, it makes sense. In a way a language with only one way of doing it is missing out on some another use case.
python and c++ both have the same API for 20-30 years I think, and they are both more robust than Java's APIs imo for 95% of cases.
Agree to disagree on that one.

Java’s standard library is one of the best stdlibs out there.

Evidence of opposite in that example:

programmers in 95% cases want something like following to read lines from text file:

for s in open('file.txt'): print(s)

Java has 5 different io APIs and no one provides such construct, you need to stack various verbose abstractions to just read text file.

How it is the best? Ergonomics and brain load is very bad.

I'm mostly write embedded code. But when I have to deal with files and associated API's I'm always like why does anyone tolerate this stuff.

  string[] lines = ReadFile("path", "filename");
Why is the above somehow hard?
actually your code is not well designed too:

- it will produce OOM if file is very large

- iterating array is usually verbose in many of languages (at least in Java)

- not sure why you would want to have path and filename as separate params

In Java, it would be more ergonomical if they add something like following:

Iterable<String> Files.readAllLines(String fileName);

How is OOM not a concern in case of C++, python? Java’s List is much more ergonomic that arrays.

Also, I really don’t understand your last example - that’s literally available in java returning a List of lines. If you add a .stream().collect(joining(“\n”)), you get the whole string in one go.

> How is OOM not a concern in case of C++, python?

I think in python it will lazely iterate through the file, and not load everything at once

> Java’s List is much more ergonomic that arrays.

right, but commenter above proposed array..

> that’s literally available in java returning a List of lines.

current API has two issues:

- OOM if file is large

- it takes Path instance which you need to construct and not "String file" parameter, thus hit on ergonomics

Path.of(yourString)
I know, but why it is necessary?

  - it will produce OOM if file is very large
A well designed API won't give you an OOM it'll throw a 'this file is too big'

  - not sure why you would want to have path and filename as separate params
Throws path not found. Throws file not found. Why does the programmer need to deal with twee-fiddly path magic for say each file in a directory of config files?

  Iterable<String> Files.readAllLines(String fileName); 
Oh sure that's totally more readable.

  I think in python it will lazely iterate through the file,
I wrote a program to suck in a list of log files and generate a bunch of metrics and display them. Users liked to select a dozen 100MB log files. Lazy iteration was way way too slow. Fastest was suck each one into memory and call split.
> A well designed API won't give you an OOM it'll throw a 'this file is too big'

this is possible solution, still it doesn't allow to iterate lines one by one and handle large files

> Throws path not found. Throws file not found.

I think it is some very niche use cases when you need to distinguish between these cases. From another hand there are many places in the code which operate absolute file paths already, and you force them to split path, which is cumbersome. At least two alternative APIs probably are justified to have.

> Fastest was suck each one into memory and call split.

I really don't think pre-splitting is any kind of performance bottleneck, it is likely 1% of performance compared to all IO stack and subsequent string/objects memory management.

Destructuring is nice, but this here seems to work only in conjunction with instanceof which is a quite rare (and somewhat smelly) operator.

I mean, how are you going to use it? Something like this?

    Point p = getPoint();

    if (p instanceof Point(int x, int y)) {
        System.out.println(x+y);
    }
... that seems pretty bad. Or am I missing something?
You'd do if (getPoint() instanceof ...) if you know it's a Point. But then, you probably don't need to deconstruct it anyway. You can just do p.x+p.y.

This type of pattern matching is mostly useful when you have some T and a few cases, there is also a switch/case equivalent for when you've got a lot of them.

Maybe we'll see some syntax for deconstructing values directly into variables, a la Python in the future.

You can have a parent class/interface with many subclasses/implementations.

You can then use pattern matching to switch on instances of those subclasses while destructuring objects to access attributes at the same time. Kind of like enums and pattern matching in Rust.

In your example there is no reason to use `instanceof` with `p`, you already know what it is.

> You can have a parent class/interface with many subclasses/implementations.

Sure, but in Java the standard handling of that is polymorphism. instanceof has been so far a bit of an antipattern, breaking out of the OOP model.

> In your example there is no reason to use `instanceof` with `p`, you already know what it is.

That's my point. This seems like a very limited version of destructuring if you can use it only when you don't know the type. Like in JavaScript you'd routinely write something like:

    const {x, y} = getPoint();
There is subtyping.

A more practical (though not necessarily realistic) example might be:

  sealed interface List<T> permits Cons<T>, None<T> {
    record Cons<T>(T head, List<T> tail) {}
    record None<T>() {}
  }


  // some function only wanting the  first elem of a list
  switch (List<T> list) {
    case Cons<>(T head, var tail) -> // do something
    default -> {}
  }
If you have used Haskell or similarly strongly typed FP language, you can see how great pattern matching can be.
> Sure, but in Java the standard handling of that is polymorphism. instanceof has been so far a bit of an antipattern, breaking out of the OOP model.

I think that's deliberate, Java is moving towards the modern programming zeitgeist that doesn't really care about strict adherence to the OOP model. This is a feature that comes from languages that don't adhere to Java's OOP principles.

I didn't downvote you, by the way. Don't know why that happened.

The idea is that you deconstruct when a method returns a sum type: An interface with a defined set of implementers that doesn't change. A very common pattern in, say, Haskell or Scala. In Scala you'd also get a warning if you forgot to check one possible type to deconstruct. If you aren't defining your data types like that, yeas, it's a bit weird.

This, along with the underscore, and even the sequence type, shows that the language designers are well aware of what Scala does, and are picking and choosing which things to bring in without risking exposing Java devs to the traditional monad salads that you see in many a Scala codebase.

You also get compile-time error in Java switch expressions if you are not exhaustive in your cases.

But yeah, of course the design team knows about features from Scala, they are probably one of the most competent people on the Earth when it comes to PLs, and are literal CS giants.

We're programmers. It's our job to know these things. There really aren't that many to learn. It's a much simpler task than various engineering fields.
the job is to produce software and make this process reliable and efficient.

> There really aren't that many to learn.

I think there are a lot to learn in java ecosystem.

The job is to communicate effectively while producing software more reliably and with less time than someone else that might get hired.

There are many of us who think Java has very little to learn - asking us to go slower - while probably good for society in general, is a losing strategy in a Capitalist economy.

You can keep up, or be left behind. Meanwhile, fortunately the “communicate effectively” is 100x more valuable as “produce software more reliably and faster” ie learning all of these new languages and language improvements.

That was literally the point of Java, to be syntactically and semantically simple, in contrast to the extreme complexity of C++. But just like with Lisp, that only moves necessary complexity to another layer. True wisdom in language design is understanding the line between necessary and unnecessary complexity, and perfectly incorporating necessary complexity into the language so users don't have to reinvent the wheel at the library or precompiler level.
Java is quite a lean language, especially considering its age, and the design team does consider every new feature for a long time before adding, being an exception among languages with regards to feature churn (with all its negatives and positives).

Just look at C# or C++, both have at least an order of magnitude larger language “surface area”.

Beyond the other posts, I wonder whether some of the reactions are due to the fact that destructuring here is purely syntactic sugar – there's no "fear of missing out" on new semantics that offer a drastic new improvement in programming style, to the extent that they feel obligatory to incorporate in your mental model of Java (e.g the introduction of anonymous functions.)
Kotlin is pretty close to this?
this was discussed here in comment several times, and opposite opinion is that kotlin added enough creatives and opinionated constructs which make it more innovative and less simple.

I think the closest thing is if someone would create linter for java which would allow only essential structures and library API. Java has very good foundational core.

Thanks I hate it
You hate that it takes 2 lines instead of 4 (excluding curly braces) to accomplish the same? The new syntax is not even convoluted, it's pretty intuitive destructuring syntax already available in many modern languages.
There's potential confusion whether the int x from the example comes from point.x (field), point.x() (method) or point.getX() (common getter pattern), just one more gotcha
By reading the proposal [1], I see it's meant for record types. Records seem to be value classes, i.e. the equivalent to Scala's case classes.

In that case, there's absolutely no ambiguity: there's only one `x` you can be talking about.

By the way, value/case classes are such a boon. Very easy to get used to, and once you have, you want to use them everywhere, I guarantee.

----

[1] https://openjdk.org/jeps/440

Thanks that makes more sense, I had assumed it was for all types as Point used in the example has the same name as a class in the standard library
And that "older Java" example itself is an alternative (added in Java 16, from looking it up) to what existed before.

  if (obj instanceof Point) {
      Point p = (Point) obj;
      int x = p.x();
      int y = p.y();
      System.out.println(x + y);
  }
Pretty much the same as C#
What is it saying here. Is the (int x, int y) from getters or the constructor? It LOOKs like a constructor, but I can't possibly imagine it's doing anything with the constructor in this call....

Which probably explains the person saying "I hate it".

No, I think this only works on Java Records where it's clear what the identifiers are.
You are destructuring the object here, and since it is a record type that is nothing more than its typed, ordered fields listed in the constructor, you just use it as its reverse function here, basically.

Destructuring for other kind of objects are planned with user-defined destructors. But it does have plenty prior art, e.g. in haskell you can deconstruct lists arbitrarily like _:secondElem:rest would bind secondElem logically.

It's a bit of an unkind example as most would write the latter as

    if (obj instanceof Point p) {
        System.out.println(p.x()+p.y());
    }
But I can maybe see the value if you need to use x/y more than once.

However in Java it's common to have getX() style accessors, for which this syntax doesn't seem that helpful as your variable would end up being called getX

I think this syntax for accessing x and y only works for java records anyways and not for methods.
For those in the ecosystem these days - is there a compelling reason to choose Java over Kotlin? I am under the impression that Kotlin is a better Java without any sacrifices. Is this true?
I mean what are your goals, its never as clear cut as X is objectively better in all use cases over Y
I still prefer Kotlin, though the gap is closing. Null checking, extension functions, type inference, and various other features are really nice.

I'm not sure what's on the Kotlin roadmap / whether JetBrains see value in making major improvements to the language.

At this point I think the more relevant question is: is there any point to using kotlin if you are not doing android dev?
Personally I find it more enjoyable to code in than Java. There’s also maybe an argument to be made around Kotlin Native, KotlinJS too.
GraalVM, TeaVM, Google’s Closure compiler. Java is an insanely big language, so if a tiny language has something, you can bet Java also has it in a much more ready form.
Sure, assuming you are happy with most of the language constructs and it feels ergonomic to you, Kotlin can make sense for backend/APIs, while still having access to all the Java libs/packages. I'm starting several new largeish projects now and have decided on using Kotlin.
Having `@Nullable`/`@Nonnull` (or its dumb `Optional<T>` friend) is nothing compared to NPE being a compilation failure

Also, all hail `val`; `final var` is objectively worse

var is write-only and won't catch type errors
Sorry, I don't understand your concern. My observation is that having a variable declaration that is single-assigment in Java requires the "final" keyword, which in the most charitable case is "final var" where the caller and/or the IDE know the type (e.g. "final var foo = new ArrayList<Bar>()") and in the degenerate case is "final ApplicationEvent<Map<String, List<String>>> thing = doSomething()"

In Kotlin one can get finality via the much nicer "val" keyword, and seems to be idiomatically the default, versus Java's mutable-by-default stance

Saying either language "won't catch type errors" is not something I can agree with

Normally, declaring a variable offers type safety. var matches anything and thus it's never a compile-time error. If someone accidentally changes the return type of getAddresses from Map to List, there is no compile-time error. There is nothing to check the return type against except Object in this case because Tuple.of takes Objects.

    final var addressMap = getAddresses();
    final Tuple args = Tuple.of("a", 200L, x);
So you may as well inline the method call and give up on any type checking.

Another example:

    final long limit = getLimit();
    for (var i = 0; i < limit; i++) {}
The wrapping behavior is determined by whether there is an L (or lowercase, l) after the 0. If limit happens to be greater than Integer.MAX_VALUE, that L is the difference between an infinite loop and a well-behaved routine. Some junior programmer could easily remove the L accidentally. And you wouldn't run into the bug until limit gets big enough. Which could happen at runtime in production. All because you wanted to save one character by using var instead of long.

This is just what I could think of off the top of my head. I'm sure there are other examples. You are absolutely giving up some compile-time sanity checks with var. If it's just some throwaway script, then fine. But if so why use Java in the first place?

I’m sorry, but you gravely misunderstand type inference. Sure, there is no compile-error if all you have is a single assignment, why would there be?

But that’s a completely useless code. Here is a real example:

  var list = getSomeFancyLongAssNestedGenericTypedList();
  list.append(new HashMap<…>());
If I change the return type of that function to something that doesn’t have a compatible append method, your method will fail. In fact, you implicitly encode all its usage in that var, so it will be maximally safe, while not being more restrictive than needed (e.g. changing to another object that has append may be fine).

Also, var is not a must-use, there are useful cases where it gives more clear code and the type is not important (say, when the right side is a constructor call), and there are cases where you really should be annotating your types. But it will be type safe either way.

Why would there be? I just showed you. Tuple accepts varargs of Object. You want a type check before you erase the argument's type to Object. Also, it's common practice to use local variables rather than inlining, just for readability. This is not some contrived example--this is an actual API that's in use by companies now.

Sure, you might append to the list. But in the world of increasing immutability, more likely the return value of getAddresses would be immutable. You don't want to rely on "well hopefully there's a method call that catches the error before it goes into the args list."

What did I gravely misunderstand? And if I gravely misunderstood it, then why can't you point out what's wrong with both of my examples instead of running as fast as possible to your own toy example? I'm actually laughing at your totally shameless pivot.

>maximally safe

You don't know the definition of maximum. Maximum means there exist no levels which exceed it. You have amnesia about the type checks that I just demonstrated which do not occur with var that would have prevented the runtime problems I just explained.

If your argument is that, meh, var is "good enough" type checking, then make that argument. Don't pretend like you can't read basic Java code.

You gave a completely unrealistic example, no one is passing around Object varargs. In the off chance, just specify the type..

I gave a more realistic example where you actually make use of your objects

Firstly, I gave two examples. Your argument to the first one is "nuh-uh" (summarized). Your argument to the second one is nonexistent.

Secondly, it's not unrealistic. It's not even realistic. It's real. R-E-A-L real. It's in production systems right now, as I said. Modern startups. If you think you can design it better, by all means. "Put up or shut up" is the phrase, I think.

Thirdly, any time you have an API that accepts a supertype of the instance in question, this will happen. Run away and hide from the truth. Or man up and admit you oversold your incorrect theory of var.

Fourthly, you didn't even address the readability issue. Which of course you wouldn't because... var. Like literally VAR. lol.

Fifthly, you still haven't come up with an answer as to what I misunderstood, specifically. Sad!

Spring Boot has full Kotlin support.
Better is subjective. Java is a great language, has a mature and stable codebase, it runs at near-Cpp speed, has built-in SNMP-like monitoring, and the developers are super careful about not breaking backwards compatibility. That's all about there is to say about it. Kotlin is also a good language for many of the same reasons, mainly because it is run on the JVM. One really isn't better, they're just different. Use the tool you know how to use, and master it.

The only category I would say is objectively "better" is Java (and other JVM languages like Kotlin) has the best IDEs out of _any_ language. There is nothing on-par for Python, Ruby, C/Cpp, etc as far as autocompletion, type inference, automated refactoring, partial recompiles, hot code swapping, unit test integration, Tomcat or server integration, code formatting, etc. Java is great because of it's IDEs. If we were writing Java in notepad.exe it'd be a pretty clunky experience, but nobody serious does that.

> The only category I would say is objectively "better" is Java has the best IDEs out of _any_ language.

Kotlin was made by the people behind IntelliJ. I'm not sure I understand your point.

sorry, I should have included Kotlin in that part of my comment. IntelliJ Kotlin is quite incredible. Every time I've needed to use it, my expectations have been met.

Makes switching to vscode feeling going from an framing nailer to beating in rusty iron nails with a rock.

extension functions can be scattered across any number of files and the import statements do not tell you where they are declared. kotlin was specifically designed to drive sales of IntelliJ
And it’s slow as molasses without feature parity.

One feature that comes to mind is rearrange methods. I haven’t used Java in years so someone who actually uses both can find more cases where Kotlin support is worse than Java’s.

That's a single IDE from a single vendor though.
We were looking at starting a new Spring Boot project several years ago and considered Kotlin over Java - but the single decent IDE made me reluctant and we went with the familiar Java instead.
The developers absolutely wrecked backwards compatibility by shuffling classes between namespaces just a few years ago. I guess the new names are somehow better, but so many things broke because things needed renaming For Reasons
Yeah the backwards compatibility point is overplayed i think. It is mostly true but The java world paused at 8 for a hair too long and when it came time to move to 11, workarounds were needed for things like JavaEE and JavaFX being removed.
While I miss IntelliJ IDEA, I’ve found Visual Studio has mostly caught up for C# refactoring. If that’s not enough for your use-case, there’s always the Rider plug-in.
AFAIK, most people using Kotlin are somehow tied to some variant of Java 8, such as Android. Kotlin is very useful for that use case, but otherwise was a useful kick for Java but I don't know if I'd start a new project in it.
Java still has no counterparts to many important Kotlin features - number one being null safety. Java is not even trying to address it (no, JSR305, checker framework, Optional are not solutions).
> Java is not even trying to address it

Valhalla might. There's been a lot of time put into nulls and primitives for valhalla. How that manifests seems to be up in the air still. But I would not be surprised to find non-nullable types in the future (like `int`).

Well yeah, but if that's null safety only for value objects, then it's still a very incomplete solution.
The latest iteration is thinking about proper nullable/non-nullable types like String?. They want to have the user care about the semantics only (whether it makes sense to have a null value here) for any kind of object, but this same notation will also give a big performance boost to value types, as those can then be stored in memory more efficiently.
There's always going to be a hole because of a legacy of not having null in the type system. Java can't simply break that.

I don't think this is something that will only apply to null safety objects. Maybe initially, but I don't see there being major restrictions to extending this to other classes.

> Java can't simply break that.

And that, in turn, answers the question of where Kotlin is different enough from Java that it might be worth switching.

Java is very strict on backwards compatibility. That has its advantages. But it also has a lot of downsides. Kotlin doesn't want/need to be backwards compatible, and it can afford to break semantics enough to make null not allowable(*) in non-nullable types.

It's a trade-off, of course. But it explains why some people prefer Kotlin.

(*) Save for Java interop.

I'm using Java 11 and Java 17 with Kotlin without issues.

When targeting Java 8, Kotlin creates helper classes for the default methods on interfaces as that is not supported in Java 8, whereas on Java 9+ it uses Java default functions. However, you can target multiple Java versions, as I've done that with my projects for 11/17 support.

I've always viewed this the opposite way: Kotlin isn't better enough to prefer it to recent Java versions.
Kotlin is less verbose, has better null safety and mutability is clearer.
I’ve never found that the differences of Kotlin matter enough to be worth it.
Those just are not enough for many of us to warrant the switch. Mutability is fine if you’re not going over thread boundaries, many of us don’t struggle with null, and brevity does not always equal clarity.
> Those just are not enough for many of us to warrant the switch.

I understand that, but for starting new projects the equation can be different.

> many of us don’t struggle with null

Sorry, but I don't believe there's anybody like that. I guess many people accepted/internalized the struggle.

Null safety at application level is just a good architecture, test coverage and regular code reviews. It is not an internalized struggle of any sort. Last time I saw uncaught NPE in server logs was a long time ago: why would it necessarily be more frequent than some data validation error?
> Null safety at application level is just a good architecture, test coverage and regular code reviews.

Anything relying on that is brittle. I work on a 20 years old code base with the majority of the base classes being 15+ years old. This is exactly where Java should shine, and it's not doing bad, but it's very hard to enforce top-notch coding standards across ~200 devs committing during that time frame.

As a result, there isn't much of an information on the nullability of return types, and you end up defensively handling nulls everywhere which is just noise.

> Last time I saw uncaught NPE in server logs was a long time ago

Luckily these are rare also for us, I guess most are caught during development and then on some test environment / CI, but catching these anytime later than compilation is way too late. The cost of a bug is directly connected with time between introduction and fixing it.

> I understand that, but for starting new projects the equation can be different.

I don't think for new projects the equation is any different. Why would I throw away a language and ecosystem that I understand, that I'm productive in and with language designers that I trust to do the correct thing for the future? Why go learn another language and ecosystem when the things it brings to the table are of no value _to me_ ?

I honestly do not struggle with null. If you actually read the code you call and test the code you write you won't have a problem.

> Why go learn another language and ecosystem when the things it brings to the table are of no value _to me_ ?

The language is conceptually very similar and ecosystem is shared to a large degree.

> I honestly do not struggle with null. If you actually read the code you call and test the code you write you won't have a problem.

Looks like you work on small projects only. If you can just read the code you call, then you don't need much of a type system at all.

In my case, the call I make often executes 10,000s of lines of code and figuring out if it can return null in some cases can take hours or days.

It sounds like badly documented code then. A function, especially if some kind of API boundary, should absolutely state whether it can or can’t return null.

Ideally everything should be non-nullable, with optionally specified nullability.

> It sounds like badly documented code then. A function, especially if some kind of API boundary, should absolutely state whether it can or can’t return null.

Of course, in the type system.

Just like we're "documenting" types themselves in the type system and not in JavaDocs.

> Looks like you work on small projects only.

Don’t be dismissive. I could easily just say “looks like you’re just a bad dev if you can’t handle null.”

I work on large code bases too. There’s nothing special about them.

It's very hard to take you seriously if your argument is "just read the code you call to figure out if it returns null or not". That just isn't feasible in large code bases.
I have worked at over a dozen companies where they were using Java. I have yet to see one where null checks and error handling thanks to said null checks doesn't occupy a significant percentage of the application. Maybe I am unlucky, or maybe you've done so much Java now you don't even read the null checks and attached error handling everywhere.
I think you’re just unlucky. Having compiler enforced null checking does not reduce the amount of nulls you need to check. They will still be everywhere and you will still have instances where people will not check null from a nullable function with !! because they believe their input won’t give them null back.
> Having compiler enforced null checking does not reduce the amount of nulls you need to check.

If a function in a null-safe language is declared to return "String", then it signals that, effectively, you don't have to check for null. So, yes, compiler-enforced null safety does reduce the amount of nulls you need to check.

The trick, of course, is to not make everything nullable, but as few things as possible.

In Kotlin, of course, this is only true up to the point where Java interop might interfere with it (because Kotlin might infer a Java return type to be non-nullable when it's in fact nullable).

Maybe our definition of significant percentage is different, but I honestly don’t find it too much.

A notable exception is some kind of generated objects, from say, wsdls getting transformed to different dtos, but in this case MapStruct is a godsend, and is overall less error-prone than doing it even in a null-safe language. (Manually copying fields won’t tell you if a new field is added that should also be copied, for example).

Kotlin also has a better standard library and collections.
Which platform?

For Android? Kotlin all the way.

Servers? Java is much closer to the JVM and Kotlin is an abstraction over it which honestly does not provide much extra compared to Java 21.

Kotlin has several design decisions which are neater 80% of time, but Java is the better general purpose language for those 20%. OC, much is a matter of taste. And in Kotlin will find yourself in awkward positions where you have to use Java Collections and you lose all the advantages of Kotlin (HashMaps not having non-nullable values is one of them).

All kotlin collection types are essentially Java ones (at least for a JVM target), the difference is that the IDE and compiler are smart about its type inference system so that it can tell when a variable use is nullable or not.

Java has come a long way in recent years and it's far better than it was in the past. Kotlin stands on the great baseline of features that Java has established and there are certainly many advantages to (mostly ergonomics), to using Kotlin over Java at least for the time being.

> HashMaps not having non-nullable values

Not sure about that example since it’s easy to do

    class MapWithDefault<K, V>(
      private val map: Map<K, V>,
      private val defaultValue: V
    ) {
      fun get(k: K) = map.get(k) ?: defaultValue
      // …
    }
Edit: or I’m sure there’s something that does this already.
Still, this is not a first-class citizen in the Kotlin space, even-though you would assume it to be the case. Introducing such custom classes is always a little icky when working in a team.
It's probably about having to box value types.
Java is still pretty ugly when you have to use the functions api https://docs.oracle.com/javase/8/docs/api/java/util/function.... I don't know why they can't make it like kotlin and scala where you just can just type the function definition instead of trying to find a class that represents what you want. This streams API for example, is unreadable.

<R> R collect (Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner)

I see the opposite. I think functional implementation of interfaces is lacking in Kotlin big time.

The fact that you see a type like Comparable<T> and can implement it with an anonymous function is unique to Java. In other languages you would have Function<Int, T, T> which simply baffles the mind. Java has a powerful and performant model which also benefits from static and default methods (e.g. functional composition).

Granted, the collect endpoint looks pretty wild but in 99% of cases you will simply use ".toList" or Collectors.toSet().

I just meant that its much nicer to read and write

(T) -> R

vs

Function<T,R>

Maybe Kotlin lacks implementing interfaces functionally but in practice, I can't remember it being a pain point. Funtional programming in Java to me, still feels very clunky compared to scala and kotlin.

Java's structured concurrency approach to async code eventually may result in much simpler libraries compared to Kotlin's approach, which introduces function coloring with the "suspend" modifier.
https://elizarov.medium.com/how-do-you-color-your-functions-...

That’s a feature, not a bug.

> We could eliminate suspend modifiers from pure-Kotlin code, but should we? I’m inclining to answer no. Having to mark asynchronous functions with suspend modifier is a small price to pay, but in return you get better insight into your code. You immediately see which functions are allowed to perform potentially long communications and which are supposed to complete quickly.

One of the most interesting things with respect to the future of Kotlin is its mission to be a multi-platform language.

When WebAssembly/gc becomes generally available, it will be exciting to see which languages will successfully target it. Kotlin has quite good chances of succeeding as a full-stack language, because of its growing ecosystem of multi-platform libraries that work in the JVM, in a browser, and on a native system. Note that the Java standard library is great, but it does not interoperate well with the JavaScript ecosystem. The same goes for C# and Rust.

As a personal pet project, and to check if the multi-platform concept actually works, I have written an emulator for an old computer in Kotlin. Initial development was done in the JVM, because I was familiar with JavaFX and sound output in Java. I then later compiled it to JavaScript, which was trivial and required no code changes, except for an implementation of canvas and audio rendering.

A serious problem is the IDE experience though. Interactive web development in VSCode with TypeScript is a breeze, with a subsecond edit-compile-run cycle. Kotlin compilation is still clunky, and fixing a typo requires at least 10 seconds of my patience, even on a decent workstation. Work is underway to improve this, but only time will tell if things succeed.

10 seconds to fix a typo? Install the IDEAVim plugin then it's simply :<line number> , w w w ..., cw. Or there's /<search term>, n n n ..., cw.
I meant fixing a typo in the source code for a user interface application, and then seeing the end result on screen.

Or perhaps I'm missing some sarcasm here :)

Java is a much bigger language, if feasible it will have much better support for wasm/native/etc. frankly, I don’t see a point of this aspect of kotlin.
Its better without sacrifices similar ways as :

1) Cloud is better than data centers without sacrifices

2) Meal kits are better than grocery shopping without sacrifices

3) Online shopping is better than going to store without sacrifices

4) SAAS is better than locally installable software without sacrifices

Some reasons that spring to mind:

- Compile speed - Kotlin is still painfully slow - about 4-5 times slower than equivalent Java functionality (rough estimate)

- Kotlin is completely proprietary, no community or openness, no alternative to Jetbrain's IDE

- Kotlin (for me) has passed from being a simpler/more terse/attractive version of Java into being a complex language with lots of strange corner cases. Particularly largish Kotlin code-bases can be hell to read as Kotlin supports and encourages a DSL like approach.

Isn't Kotlin licensed as Apache 2?
Indeed. It's hard to take someone seriously when they call a language proprietary when it's been open source since inception and then claim it's 4 - 5 times slower in compilation speed and then provide no metrics to back up their point.
Fwiw the compiler is definitely slower. It’s a big selling point of Kotlin 2.0 because they rewrote the compiler to be faster.
4-5 times slower? That estimate is indeed rough. For some workloads Kotlin's function inlining creates more efficient bytecode than Java.
He's talking about compilation speed, not execution speed.
Still, 4-5 times is absurd. I've used both Java and Kotlin within IntelliJ and the compilation speed difference is marginal.
Using Java means going with the less fuss default (instrastructure and support) wise. And the gap is closing in.
Kotlin has a less good backward compatibility record and interacts awkwardly with newer JVM/standard library features - e.g. streams are cumbersome to work with because Kotlin has a different approach to absence/nullability. Async in particular is a place where Kotlin has gone through multiple incompatible rewrites and is doing something that's not necessarily going to align with the JVM way of doing things in the future (Project Loom - which I have my own doubts about, but if you believe in it then that's definitely a reason to use Java over Kotlin).

(Plus if you're going to go to the trouble of using a non-Java language it would be a real waste to use Kotlin over Scala)

> streams are cumbersome to work with because Kotlin has a different approach to absence/nullability

I'm not sure that's a reason to pick Java over Kotlin though – streams are just generally cumbersome, even in Java, compared to Kotlin's sequences. By choosing Java you're getting more exposure to them whereas in Kotlin you can mostly avoid them in favor of sequences.

Streams are less cumbersome in Java because you can use optional everywhere and it's relatively idiomatic; the ecosystem (at least the modern parts of it) will support you in doing so. Whereas in Kotlin everything expects you to use their special nullables and so you have to constantly shift back and forth.
I've had the opposite experience. I've found using Optional/map everywhere harder to read than Kotlin nullable types. And I find Kotlin streams/streaming API easier to read and use/more intuitive than Java's equivalent.
Kotlin is the coffeescript of Java. Kotlin is actually not a great thing to write with or learn as Java has caught up. (this example speaks over javascript and coffeescript battle where coffee was eventually killed by native improvements).

I would not start Kotlin or learn it unless you go back 5 or more years.

IMHO, there are certain developers who've always liked Java exactly as it is. They view Kotlin as too hipster-y, sugary, complex and changing too fast. I think those people should just continue to use Java.

Then there's other people like me who come from other ecosystems. I used to write Ruby and I just can't stand how verbose and convoluted Java can be. It makes reading code a real pain. But with Kotlin I found a language that struck a good balance between expressivity and compile-time safety.

Maybe Kotlin should do more to convert people from other ecosystems rather than trying to cater to the kinds of people who were never offended by the dullness of writing Java.

It remains a bit bitter sweet to see that so many features that we’ve had for 13+ years in Scala and longer in languages like Haskell and were ridiculed back then, now find their way to mainstream Java.
Ridiculed? Really?
The only Haskell feature I can think of people ridiculing is laziness, which makes sense because it sucks and makes performance hard to reason about.
They only ridicule it because they don't use it or understand it.

Laziness by default is my favorite Haskell feature.

So you've never had a space leak? Be honest...
I've seen lots of things and problems in languages both with and without lazy evaluation.

On the whole, I like it in Haskell. In languages without lazy evaluation (by default) I tend to miss it.

I've never seen it "ridiculed" by anyone proficient with the language or who used it for an actual project, either.

Great non-answer.

Meanwhile, no honest Haskell developer can claim to have never been surprised by a space leak at one time or another.

Why?

Becauze laziness is simply harder to reason about.

It's a lot like coding in something like Prolog, in that you need to internalize not just the behaviour of the program, but the behaviour of the Haskell runtime as well, and how laziness is actually implemented.

This makes it very challenge to reason about both space and time reliably, even for a seasoned developer.

The fact that whole tutorials are dedicated to picking the right fold so you don't leak space is a perfect encapsulation of the problem.

And for a language that emphasizes compile-time safety, it's a major class of bug that neither the compiler nor runtime help the user detect.

I've worked in many different languages and runtimes over the years and I've never encountered another feature that was equally complex.

> Great non-answer. Meanwhile, no honest Haskell developer can claim to have never been surprised by a space leak at one time or another

I did answer. I've been hit by them. In general, the problems are fewer than without laziness, though. I've seen more problems in production caused by careless eager evaluation.

Are you an "honest Haskell developer" by the way, that you speak for all of them? "Be honest" in your answer, please.

> Becauze laziness is simply harder to reason about.

No, it's harder to reason about some aspects and easier to reason about some others, like gluing together code.

> I've worked in many different languages

But not Haskell, right? "Be honest".

I've never seen laziness ridiculed (not critiqued, but ridiculed, remember the context of the conversation you're in) by an actual practitioner, have you? "Be honest".

> But not Haskell, right? "Be honest".

I absolutely have worked with Haskell and written a number of non-trivial programs in it, including a bytecode interpreter for a toy virtual machine.

I also spent quite a bit of time writing solutions to various coding challenges in Haskell, which gave me an appreciation for how hard it is to write high performance code in the language (anything touching strings, in particular, was a real joy...). I've never felt like I had to fight the runtime quite the way that I had to fight it with Haskell.

But yes, if I'm writing normal, run-of-the-mill, low complexity code, it's pretty easy to get Haskell right. But then in that circumstance it's easy to get it right in any language.

Of course, why would you believe me? You're already so sure your opinion is fact that I doubt anything I have to say will sway you.

No, I believe you. Why wouldn't I? It's you who accused me of giving non-answers and asked for my "honesty".

Fair enough. Haskell developers do not think the language is perfect, and I've seen a fair share of critiques, but I've never seen laziness "ridiculed".

And since I believed you, believe me: whenever I deal with languages without easy recourse to lazy evaluation, I miss it. Every expression is clumsier.

> You're already so sure your opinion is fact that I doubt anything I have to say will sway you.

Are you a mind reader?

I suggest you go back and look at your first reply to me. Do you think it was crafted to elicit good mannered conversation?

> I suggest you go back and look at your first reply to me. Do you think it was crafted to elicit good mannered conversation?

Yup, that's fair, I came out swinging because I read this:

> They only ridicule it because they don't use it or understand it.

As belittling the intelligence of people who didn't share your opinion.

If that wasn't your intention and I read too much into your comment, then I absolutely apologize for jumping down your throat so aggressively.

I'm sure you can find a flame war or three about pattern matching. Oh boy, the threads you'd have 10 years ago about this stuff.
Yes, ridiculed. E.g. someone posting (presumably trolling) /r/scala today: "I just wonder how can people think something as arcane and absolutely out of this world as the typelevel stack or ZIO can have a place in the industry."
I also wonder if perfection is reached when there is nothing more to add ;-) Current popular languages seem to converge.
One major upside of newer languages like rust is that they can start out with these "newer" primitives, with a standard library designed around them and without tons of "legacy primitives" that do the same thing, but worse (like how python has 4+ distinct ways to print a string with variables in it, all in various degrees of popular use).

Once your language has aged a little it's hard to impossible to take old primitives away, but you still want to adopt ergonomic improvements in order to not be left behind. Until at some point you become a language nobody can master in its entirety.

They seem to be converging on Scala :)
They're converging on Kotlin, but of course 75% of the difference between Java and Kotlin consists of Scala features that have been laundered of their dirty "academic" origins for acceptance in polite industry.
> laundered of their dirty "academic" origins

curious; can you give a few examples? TIA

Kotlin extension methods are basically just type classes but without the theory.
Uh, I don't get this. Extension methods are ultimately just syntactic sugar, everything written as an extension method could just be written as a top-level function taking the receiver as an extra parameter.

I would say the analogous feature to typeclasses are interfaces.

To be clear: I speaking somewhat in jest, and I'm not really talking about academic versus industrial programming. I'm snarkily referring to the fact that that many developers didn't see the value in a lot of Scala's features and labeled Scala as an impractical, over-complicated academic language. And yet: Kotlin has seen great success by spoon-feeding Java devs a lot of those very some features (with the benefit of second-mover advantage).

In terms of actual examples, I'll point to JEP 443, linked in the Java 21 article that kicked off this topic. That feature comes from Scala, by way of Kotlin.

And it'll be another 5 years before JDK21 becomes a widespread target for production and then another 5 years for the new features to be accepted in code reviews. Culture changes slowly.
Think SpringBoot might just change that. Folks are scrambling to move to 3.x, with 2.7.x hitting public EOL this November. With SpringBoot 3 requiring Java 17 at a minimum and Java 21 coming out this fall, I suspect our shop will see many people just move to the latest LTS when it is available.
I think it'll be much faster this time around.

We took way less time to move from Java 11 to 17 compare to the 8 -> 11 migration due to the change in deployment methods: instead of deploying jar file and run it with the server-provided JRE, we now use docker to ship our app + JRE to server.

On the flip side, how many features are in those languages that java benefited from not introducing?
I can only think of XML literals (which Scala did eventually manage to remove) and the first version of value types. (Plus some new things in Scala 3 which time will tell about). From Haskell maybe Python-style list comprehensions (Haskell has two sets of syntax sugar for using monads more easily and Python took the bad one).

Pretty poor tradeoff on the whole if you ask me - but Scala is always there for anyone with enough sense to pick it up.

This feels like Option/Optional all over again.
One might say that the general developer community has only recently became ready for many FP ideas.
The syntax for the string template surprised me. Are there other languages that use a `STR.` prefix to indicate string formatting?
Python requires a prefix too f"{x} + {y} = {x+y}". I guess you can now almost simulate it by using f."\{x}...".

The new java way is strange, but maybe it was to avoid having to touch the language parser too much.

the Java one is pluggable, whereas if one wanted to implement the cited PreparedStatement-strings in Python it'd be a parser bump since there is no `sql""` in the grammar
What I wanted to mention is that maybe (although probably not possible with the current grammar) an alternative would have been to remove the dot.

  STR"x=\{x}"
The "STR" isn't syntax, it's the name of template processor the user has chosen to process that template.

Users can define their own template processors to control how the interpolation behaves: sanitize inputs, etc.

> Users can define their own template processors to control how the interpolation behaves: sanitize inputs, etc.

This part honestly seems like overengineering. Like Java architects resisted operator overloading for decades, and now they pull off this in the first version of templated strings ...

I disagree.

This has existed in JavaScript and Scala for years, and is a very useful feature.

Like for SQL queries:

  sql`SELECT * FROM account WHERE name = ${name}`
Java is a much more conservative language than either JavaScript or Scala. It seems like almost DSL-like feature which is very uncharacteristic for Java.

Personally I write JavaScript daily, and I've never seen this feature being used.

Even this example - will this create a prepared statement or insert a correctly escaped value into the SQL string?

Either way this seems pretty niche. Are there some major use cases for it?

> Personally I write JavaScript daily, and I've never seen this feature being used.

I see it be used for SQL specifically in loads of projects.

https://www.npmjs.com/package/sql-template-strings

https://www.npmjs.com/package/squid

> will this create a prepared statement or insert a correctly escaped value into the SQL string?

Always prefer server-side parameterization. If you are referring to java.sql.PreparedStatement specifically, that requires a connection, so this is only an intermediate value to creating that.

You mean you don't see tagged templates used at all? Or just not for sql?

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...

> Even this example - will this create a prepared statement or insert a correctly escaped value into the SQL string?

That tagged template doesn't evaluate to a string, it evaluates to a parameterized query object that contains the parameterized query string and the parameter values. You pass it to the mysql library and it executes the parameterized query.

  const query = SQL`SELECT author FROM books WHERE name = ${book} AND author = ${author}`
  typeof query // => 'object'
  query.text   // => 'SELECT author FROM books WHERE name = $1 AND author = $2'
  query.sql    // => 'SELECT author FROM books WHERE name = ? AND author = ?'
  query.values // => ['harry potter', 'J. K. Rowling']
> You mean you don't see tagged templates used at all? Or just not for sql?

Not at all.

Thanks for the explanation, so it's not just escaped param substitution.

Which brings up the question if you can combine normal text substitution with SQL substitution, e.g.:

   SELECT author FROM books WHERE name LIKE '%${book}%' AND author = ${author} AND ${extraCondition}
(it's pretty common to have to join query segments)
The text processor does have context as well, so there is nothing preventing someone from doing just that. Not sure if there will be a standard SQL processor, though.
(comment deleted)
the library recommends you do that this way

  const query = SQL`SELECT * FROM books`
  if (params.name) {
    query.append(SQL` WHERE name = ${params.name}`)
  }
  query.append(SQL` LIMIT 10 OFFSET ${params.offset || 0}`)
or if you need raw values like a table name then just pass append a string.

  db.query(SQL`SELECT * FROM "`.append(table).append(SQL`" WHERE author = ${author} ORDER BY ${column} `).append(order))
> Even this example - will this create a prepared statement or insert a correctly escaped value into the SQL string?

It runs the sql function, with separation between the static string parts and the dynamic values, and it returns whatever the sql function returns.

I strongly disagree. I work on Dart, which has had string interpolation since before Dart 1.0. And, also, since before Dart 1.0, I've wanted to have support for custom string interpolation processors.

It's just really useful if you can have an API that can decide how interpolated stuff gets handled. You can santize, do custom formatting, lazy evaluation, all sorts of fun stuff.

> Java collections don’t have a type representing an ordered sequence of elements, Java 21 fills this gap by introducing the SequencedCollection, SequencedSet and SequencedMap interfaces. These interfaces provide methods for adding, modifying or deleting elements at the beginning or end of the collection, as well as for iterating over a collection in reverse order.

That sounds like a deque, and it's been in Java for a long time. (No set or map implementations though.)

https://docs.oracle.com/en/java/javase/18/docs/api/java.base...

(comment deleted)
Some of the methods were explicitly lifted out of the Deque into this interface. Basically the aim of the new interface is to provide an unified experience and standard methods to all the collections that can feasibly support it.
Anyone knows when the Java 21 LTS certification book will be out (“OJP exam” or something like that)?

I’m tempted to pass the exam with Java 17, since the wait for Java 21 will be too long, assuming it will be several months after the GA date, September 21st.

You'll probably be fine without it. If I'm interviewing someone and they have a Java 6 certification I'll just think that someone forced them to get it, but if they have 17 or newer it would be a big red flag unless they are straight out of uni and working at a consulting firm.
For a while I was only interested in newer Java releases just to see if Runescape ran on it :p

The usefulness seemed to cut-off with Java 11 though and now the RS Java client is being deprecated.

I can't wait until major frameworks (such as Spring in version 6.1) support virtual threads. Reactive programming (e.g., Webflux) would become a niche (unless your use case truly requires streaming with a high number of TPS).
> JEP-448 – Vector API: sixth incubation of this feature. This new version includes bugfixes and performance improvements.

Rant:

JFC Java, its 2023 and people use Java to run actual databases and develop machine learning infrastructure. Yet the ability to do SIMD in the JVM is on its sixth incubation.

That's because they want to get it right. The same with Valhalla. The same with Loom.
I'd rather have a good API late than a bad API early in a language as committed to backward compatibility as Java.
another important feature is Arena api which is also in preview state
Well, being a portable platform is hard.

Even for native platform like C/C++ or Rust, SIMD intrinsic took a while to develop, and required multiple iterations to mature for each supported platform. Plus, with newer CPU instructions being introduced, the job is never fully done.

> Yet the ability to do SIMD in the JVM is on its sixth incubation.

As noted in the JEP. The Vector API cannot be move to preview before value classes are available. Hence it is still in incubation and will be for quite some time.

Java 21 is great but every enterprise java system I've seen is still stuck on JDK 8
We are rapidly moving millions of LOC onto Java 21 at $work so that we can take advantage of virtual threads. It's been relatively straightforward as we were already on Java 11 in most places (and on 13, 17 in other projects). The project is like the definition of "entreprise".

The move from 8 to 11 was way harder than upgrading from 11 to anything else. In general Java compatibility between versions is very good.

With Application Servers (Oracle WebLogic, etc.) rapidly approaching EOL support companies are migrating their codebases to Java 11+. Additionally, Java 8 reached EOL on Mar 31, 2022. Any companies still using it must pay Oracle for extended support. This too is ending soon.
Is Java 21 the next LTS version?
(comment deleted)
Yes, but: https://twitter.com/nipafx/status/1676908785313492992

"JDK21 is a version, for which many vendors offer support."

tl;dr: the Java version is not what gets "long-term support", it's a specific runtime JDK released by some Java vendor, e.g., Oracle, Eclipse Adoptium, Azul, Microsoft, Amazon (Coretto), etc.

Would be cool if there were an easy way to add primitives to Java. I would like a 128-bit integer that the JVM could handle with `add` + `adc` and friends. If I could extend my JVM trivially by linking in a `.so` that enabled it to handle the primitive it would be pretty sick. I imagine that's not really that easy to do, but a nice wide primitive that could be used for fixed point arithmetic would be nice to have.
Are StringTemplates constant? i.e. for this method:

StringTemplate foo() { return RAW.""; }

is foo() == foo()?

This is a very useful feature of JS tagged template functions, as it lets a template processor do expensive one-time processing of a StringTemplate and cache the result, then use that to quickly apply that to particular values. I don't see an answer here: https://cr.openjdk.org/~jlaskey/templates/docs/api/java.base...

Did tail call optimisation make it to the JVM so that we can have it in Clojure?
No, still on the drawing board.
Is there something like pytorch or tensorflow for doing deep learning with Java nowadays? Can Java be used to create programs running on GPUs?