Nullable vs Non Nullable Types

The null type is often called the “absurd” type becuase it is a subtype of all types, which doesn’t seem consistent for a value. This means that you need to track the kind of null it is in order to recover the actual type and maintain type safety. I think this is mostly an issue for determining the static type of an expression, and related issues.

Couple of months ago, I wrote a Medium article that compares Kotlin’s Nullables with Optionals in Java. It is quite fitting to the topic of this thread:
“Kotlin Nullable Types vs. Java Optional” Kotlin Nullable Types vs. Java Optional | by Fatih Coşkun | Medium

1 Like

Nullable types null values do not hold type information on JVM the same way JVM Optionals do not hold this information. Due to type erasure. In theory, Kotlin on another platform could hold this information. As for why you can need it? No idea. I never saw a point of extracting type of null. In those cases it needed, it could be done by compile time type checks. Rust has a different type system with much narrower support of OOP, maybe something is different there.

As for JVM Optionals, I am currently moving java code to kotlin and rewriting Optionals as nullables. It is not only more concise, it is in fact better optimized since avoids additional boxing.

1 Like

I am not sure that is the defining trait of an “absurd” type. As far as I know, the absurd type of a type system refers to a type with no values, which means it is impossible (absurd) to have a value of such a type. It is, however, true that a type hierarchy often has an absurd type at the bottom (btw, null is not at the bottom of the type hierarchy as it is not a subtype of primitive types). In Kotlin, the absurd type would be None.

Not true. Android compat libraries starting from version 27 or 26 have @notnull and @nullable annotations everywhere. It works perofectly with Kotlin nullable types.
Also I personally hate Javwa Optional, since it’s much easier for me to read Kotlin nullable types accompanied with standard language operators like .? and :?, than reading a functional goulash of piped method calls on some Optional

1 Like

Nothing is the subtype of all types in Kotlin. There are no values of it.

“Absurd” is just a name, but in type theory there is the top type and the bottom type, top being “anything” and bottom being “nothing.” The top type is a ancestor or supertype of all types and contains all possible values, and bottom is a subtype or descendant of all types and contains no values. That is the theory.

Programming languages need to create concrete implementations of these abstract theories, so there is often an actual value for “nothing” which is null or nil, usually just for reference or pointer types. In languages that require all expressions to have a type and have a null literal value would end up having a defined type that contains only the value null.

Primitive types may logically have null values even if they’re not actually defined for a language. Personally, I define a null value for each primitive type for use in my code.

I wouldn’t call a type that contains no values absurd, it just has limited uses. In C, void is such a type, which happens to not be the same type as null which is a pointer to void, but that’s C for you.

I know the Bottom type from type theorie, but you got this part wrong: null is rarely a value of Bottom, especially not in Kotlin. In Kotlin Nothing is the Bottom type. And just like in type theorie Nothing is the subtype of all types, and there are no values of it. You cannot assign any value to variables of this type, not even null.

Just like Bottom in type theorie, Nothing in Kotlin has very interesting implications. For example, you can define a function with return type Nothing. What can it return? Nothing, no value, no null and not even Unit. The only way to implement such a function is let it go into an infinite loop or let it throw an Exception.

What are the applications of such? Consider the following example:

fun todo(): Nothing = throw Exception("Not yet  implemented")
val number: Int = todo()
val text: String = todo()
val number2: Int = number*2
val text2: String = "$text and $text"

This compiles without compilation errors in Kotlin. Because Nothing is a subtype of Int as well as a subtype of String.

That’s true in Kotlin due to the implementation, but in other languages the type of the value null is bottom, at least functionally. For instance in Modula-3 the literal NIL (equivalent of null) has type NULL and is assignable to pointer references, traced (GC’d) references and procedure types even though none of hose things are assignable to each other. So the NULL type is acting as bottom.

What is your point? Is this just another weird discussion of computer science theorists, which could never be applied in practice? The bottom line is that:

  1. There are nullables in the kotlin type system.
  2. One could work in kotlin without nulls by intercepting them in the place of origin and using only non-nullable types.
  3. One does not have to do that since explicit nullable is very convenient replacement for optionals.
  4. The idea of kotlin type safety is not that you do not have nulls, but that you intercept null in the place where it is assigned, not where it is requested.

Please do not create useless YAMT (yet another mega topic) out of that.

We are just Kotlin enthusiasts that love sharing the enthusiasm by discussing topics here. But your post does a good job of diminishing that enthusiasm. Sorry.

No complaints here, bytes are cheap and anyone could just not read what they do not like. I just do not understand, what is the point of discussion. Initial proposal by @nutjob2 seems to be absurd if I understood it correctly. And now the discussion came to type theory. The only rational point I see is that kotlin nullables indeed do not store the information about its type in the runtime (if they are nullls). Still, I can’t think of real life example where you can need that information. Usually, if you want the type of something and kotlin does not want to give it to you (you can’t get the runtime nullable object), you forgot to declare it as :Any in generic.

Once upon a time I had to deserialize some weird data in which, as it turned out, value at some key could be anything, including null and not being present at all (while originally expected to only be some specific type). Method which I used to get values by keys had return type Any? so that returned value had ambiguous type. Of course code was working just fine as long as such value had expected type or was null. But once it was present and had unexpected type (which happened inevitably) code failed. It took a few moments to realize exactly why.
Maybe not the most convincing example, but a real life example nevertheless.

Theory guides practice and provides rigor. Getting down into the nitty gritty of what language elements and code means exactly is how you avoid subtle errors and bad design. It’s not some weird theory, it’s a practical discussion on how null values are integrated into the language.

Languages and compilers are built on top of all kinds of theories and wouldn’t exist without them. Why you’d want to reject discussing them I’ll never understand. Just fiddle with the code until it works, hey?

Hi…
I still keep my opion about that “null” is unavoidable because at least you have to think about it. In kotlin even expressed by the type system. So I have to make my decision which type to choose, e.g. Foo or Foo?, which I can not ignore… Else explain me please what do you mean about “avaoidable”.
On the other hand, I read about the type system making the code safer. I don’t think so… My experience says me; the only way to make code safer is TESTING. And that means knowing about programming techniques/desciplines, common software architectures or patterns makes code far safer.

My personal conclusion: It doesn’t matter which programming language you choose; you have to test your code base! I would not prefer kotlin over java because it is “safer”. No, I would prefer kotlin because its syntax and features are more beautiful to me. But testing I can not avoid neither. I would like to have kotlin less restrictive, no more.
Just last question… What happens, if my decision was wrong about “Foo” and I have to change it to “Foo?”? How much code I have to change?

For the first part of your post. It is not quite correct. Testing is important, but amount of testing in, say, Python, and Java are incomparable. Type system protects you from common pitfalls and it is very important. Also there is such thing as debug time. When you catch a NPE, it is quite obvious that something is wrong, but it is not always easy to find the problem itself. Kotlin solves that by nullables in the type system.

As for the second part, if you replace nullable by non-nullable type, you need just to add check in the place, where you assign the value and remove checks (!! ?. and ?:) where you use it.

You should remember that nullables in kotlin have meaning, they are values that could have undefined state, not the ones developer forgot to assign. You should not use nullables in cases, where it is just inconvenient assignment.

I think the argument being put forward is either (a) that nulls are avoidable becuase you can have the language stop code from passing or assigning them (the head in the sand strategy) or (b) you don’t need nulls becuase you can use something like optionals, which are the same thing (they have a “nothing” side in the place of null values) with some additional language support or safety or maybe (c) it’s all just a terminology problem.

At the end of the day there will always be null values, no matter what people call them or which code bans them.

Yes as long as a language has any concept like a pointer/reference or whatever you want to call it there will also be a concept of a “null” reference. Can you write a program which does always guarantees that all references always point to a valid object? Probably, but I don’t think it is something you can practically do in any non trivial program.
Still I kind of agree with the statement “nulls are avoidable”. Not in a way that you can avoid having a concept of null, but in a way that you can avoid nulls in places where it would lead to a NPE.
I don’t have any real numbers but I can comfortably say that I have at most one nullable variable for 10-20 non nullables. That means that my code can can throw NPEs only 5-10% the times the same java code would.
Ok thats not quite true, I use lateinit and sometimes as we established constructors can lead to NPEs in rare cases, but this still cuts the time spent debugging down a lot.
The places where I have to use null to represent the absence of data, I also don’t get any NPEs because kotlins type system ensures I don’t forget about checking for null.
So yeah, I don’t remember the last time I had to deal with an NPE in kotlin and I think that is manly because of the type system.
Did I encounter many NPEs when using java? Not really, most of them where found by us during the development and my old workspace had some strict testing rules, but still there where IMO too many issues related to NPEs and tracking them done is hard. When the code does not define where a value is allowed to be null and where it can not be null it is quite hard to know at which point the null is expected and when it starts being a bug.
This intention stated by the programmer in kotlin allows others to make better and faster assumptions about the code which in turn leads to faster development and fewer bugs.

Not more than you would have to change in java. Imagine you have some java code and the expected behavior is that your foo is never null. Do you have null checks all the time for it anyways? I don’t think so. You might have at some points just to be safe, but there are some places where you don’t because you forgot or you did not care. Then later you decide to refactor your code to allow for null. You add the checks everywhere or so you think, you just forget about one tiny place. A month later you get a call from your customer because their program crashed. Kotlin would have told you about that.

Yes, but do you write a unit test for every single combination of parameters of each function testing for nullability? I don’t think anyone does. Kotlin has this kind of unit test build into the language. Is it right 100% of the time? No, but I’d say it is 99.99% right. And this is what unit tests are about. Letting you develop code and warning you about possible errors.

There is a simple example. We have a map and retrieve the element by key. Obviously, this method could return nothing and you need a way to treat it. In Java you can wrap it in optional and treat it later. It is OK, but the code does not look so good, also java does not force you to wrap it. In kotlin if you ever have to assign some non-nullable value to this result, you have to either make explicit check, or double-bang it and expect NPE in this moment (you do not need to think later, where is should occur, you know exactly in advance).

But you still have nulls or their equivalents and the only difference is that you’re forced to do checks or whatever by the language, and to me that generally makes programming more verbose and requires more “safety busywork.” I love safe languages but some things go too far.

The problem for me is that its generally not cut and dried whether a null value (or its equivalent) is allowed. Generally the functions I write happily accept and pass along nulls and only at some point, determined dynamically, are the nulls invalid, usually becuase of the value of some related data. So I can’t write static declarations that govern those nulls and I have to allows nulls almost everywhere. It’s a similar situation for values inside objects.

To me nulls are not special in terms of checks, I put in various asserts at the start of functions that check for inputs or combinations of inputs that the function is not able to handle, not just nulls. Having special language features and restrictions just for nulls seems like overkill, especially since the problem is bigger than them. They seem like a crutch for people who perennially forget to check for nulls.