Hacker News new | past | comments | ask | show | jobs | submit login

Kotlin does indeed (mostly) fix null.

Kotlin does not fix the issues with checked exceptions. It gives up and gives us nothing for error handling. So, for all the beauty and magic of a strong static type system, I have absolutely no idea if `fun foo(i: Int): Int` is just going to crash my program when I give it -1.

"Do you have a fatal error? Throw an exception."

"Do you have a non-fatal error that's totally expected as part of your API? Throw an exception."

"Do you want to cancel a coroutine? Throw an exception."

"Do you want to define a class and validate the parameters you pass to the constructor? Screw the type system! Write an init {} that throws an exception!"

Kotlin also doesn't really "fix" Java's lack of unsigned ints. The implementation that Kotlin provides is really poor. That's partly because of Java's lack of unsigned ints, so it's not entirely their fault, but it's a really bad API and going between signed and unsigned ints is very bug-prone. They also don't work with serialization libraries because they're implemented as inline classes, which don't work with serialization libraries.

Kotlin doesn't have the issue with array variance because it doesn't really have arrays like Java has. So that's good.

With respect to interfaces vs. type classes. The issue is this: let's say you're writing an API and you decide that you want to define an interface. Let's define it as `interface MaybeEmpty { fun isEmpty(): Boolean }`. So in your API, you might have some function like `fun foo(maybeEmpty: MaybeEmpty) { /* do something with maybeEmpty.isEmpty() */ }`

See where this is going? You realize: "Hey! I want to implement `MaybeEmpty` for a bunch of types to use in my function."

How do you implement `MaybeEmpty` for `String`? What about `Collection`? You can't. What you have to do in Java+Kotlin is define at least two new classes: `class MaybeEmptyStringAdapter(val value: String): MaybeEmpty { override fun isEmpty() = value.isEmpty() }` and `class MaybeEmptyCollectionAdapter<out T>(val value: Collection<T>): MaybeEmpty { override fun isEmpty() = value.isEmpty() }`.

Then when you actually want to use a String or a Collection in your fancy code, you have to write:

val s: String = getSomeString()

foo(MaybeEmptyStringAdapter(s))

With type classes, which exist in Scala, Rust, Swift, and Haskell, you can extend types with interfaces AFTER their definition. So I could implement MaybeEmpty right on String itself and then just pass the String value right into my function. No extra classes, no extra wrapping, no performance overhead.

Kotlin has extension functions, which are a neutered version of type classes.

Kotlin does not support reified generics in classes. They can only be used in inline functions because it gets transpiled into the equivalent of `fun <T> foo(clazz: Class<T>)`. The JVM is not going to get reified generics any time soon. It's been in the works for years and years and will probably break things.

Java's dates and times depend on a global, mutable, timezone setting. The datetime classes use it pervasively. Dealing with Calendar is awkward as heck. The whole TemporalAccessor interface is madness- you never have any idea what method is going to throw an exception for a given implementer. JDBC's dates and times are utterly broken because they use the old Java Date class and it will never change.

JDBI relies on JDBC, so I'm not convinced it actually fixes the problems other than having a nicer API. I stand partly corrected: "By default, SQL null mapped to a primitive type will adopt the Java default value. This may be disabled by configuring jdbi.getConfig(ColumnMappers.class).setCoalesceNullPrimitivesToDefaults(false)." So at least it's only wrong by default...

So, it seems to me that Kotlin actually addresses only two things on my list of complaints: null and array type variance.

EDIT: I accidentally forgot about immutability. Kotlin doesn't have that either. `val` doesn't mean "immutable", it means "not reassignable". I can 100% mutate the ever living crap out of:

class Foo( val inner: MutableList )

val foo = Foo(mutableListOf(1))

foo.inner.add(2)

Look at all those vals! Not a "var" in sight!




Just as an additional detail: Java has a new DateTime and related classes, that I believe solves every(?) problem with the old Date class.


Well, Java 8 isn't really "new" anymore. :)

But, I was complaining about the 8+ DateTime API. Now, granted, it's WAY, WAY, better than the old API. And it's much better than what's available in at least several other languages (cough JavaScript with no time zones at all).

It still depends on a global, mutable, time zone variable. It uses it in several places where it's kind of hard to get it to just use a given time zone.

The API is also just really hard to discover and use correctly. Look at what classes implement TemporalAccessor. It's not easy at all to guess which methods will throw an exception for a given implementer. Further, what if your function is just given a TemporalAccessor? I have NO IDEA what I'm actually able to call on the object without it exploding.

Calendar is awkward and hard to use.

Switching around between TimeZone, ZoneOffset, and ZoneID is pretty cumbersome. Some stuff takes Strings, some take ZoneID, etc. Some things that seem to take ZoneID don't seem to be able to take a custom TimeZone (not that I think I ever really needed that- I was trying to do something hacky IIRC).

A lot of methods take Long, which you have to just read the docs to know if that's seconds or milliseconds.

It's just kind of a bleh API.

Then you mix in the fact that JDBC is forced to use the old Date classes and that messes everything up because of timezone crap.




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: