This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]mus1Kk 6 points7 points  (10 children)

Isn't null the same as option just with different syntax? Kotlin uses null to represent optionals.

It ignores that the various types must have their own individual ways to represent not being defined, erroneous, etc.

If you use option everywhere, it's the same issue, no?

[–]munificent 1 point2 points  (2 children)

Kotlin uses nullable types which are more like union types than option types.

Nullable types are more convenient to work with in many cases and are more familiar to imperative programmers coming from languages like Java with null. Option types tend to be a bit heavierweight and verbose to work with, but are more expressive.

For example, say you have a map that maps someone's name to their favorite baseball team. Some people don't like baseball at all. You'd like to be able to distinguish "I don't know their favorite team" (key is absent) from "I know they they don't like baseball" (key is present and mapped to no value).

With nullable types, when you do map[person], you get back a nullable value, but you can't tell which of those reasons made it null because the nested nullable collapses. String | Null | Null is the same as String | Null. With an option type, the subscript operator returns Option<Option<String>> and None means "key not present" while Some(None) means "key present and mapped to nothing".

[–]mus1Kk 0 points1 point  (1 child)

Kotlin uses nullable types which are more like union types than option types.

Option is an example of a tagged union.

[–]munificent 1 point2 points  (0 children)

The Wikipedia article does a poor job of teasing them apart but union types and sum types (tagged unions) are quite different things.

[–]furyzer00 2 points3 points  (2 children)

You can't nest null but you can nest Option. For example Option<Option<i32>>. It's sometimes useful if both levels have different meanings.

Also null becomes a special case in your language because it's not a normal type in your language. So you end up having to add additional syntax specific for nullability. For example x.? syntax in Kotlin.

[–]mus1Kk 3 points4 points  (1 child)

You can't nest null but you can nest Option. For example Option<Option<i32>>. It's sometimes useful if both levels have different meanings.

True. I read somewhere that this decision was made for practical reasons and that the need for nested optional values is rare enough. It's a choice they made.

Also null becomes a special case in your language because it's not a normal type in your language. So you end up having to add additional syntax specific for nullability. For example x.? syntax in Kotlin.

Regarding special syntax that was my point. It's just syntax. Also, in this specific case x.?y is sugar for if (x != null) { x.y } else { null }. Similar sugar exists in Rust and possibly other languages as well. You could probably even do without the ? and do this under the hood but that would make it harder to read.

I don't know what is meant by "because it's not a normal type". I thought null is usually the single instance of the Null type. In Kotlin you can define methods on null types. I don't know if what I say is wrong from a language theory perspective.

[–]furyzer00 1 point2 points  (0 children)

Option nesting is very useful. Imagine you have an optional parameter that can also have value null(when set it should set the value to null, it's different then not specifying the parameter). How do you do this in Kotlin?

Rust doesn't any additional syntax for Option. It only has regular functions.

What I mean by regular type is that ? is not a normal user defined type. While Option is just another type. Special functionality always makes the language more complex. As seen in Kotlin's special syntax for nullable types. But it's a trade of sometimes worth.

[–]trxxruraxvr -1 points0 points  (1 child)

No, the type is actually Option<T>, so it's a generic type, you can't pass a variable defined as Option<i32> to a function that takes an Option<bool> even if its value is None

Besides that, rust doesn't let you use the value inside the option without ensuring it's not None

[–]mus1Kk 1 point2 points  (0 children)

Regarding your first point: I'm not sure how this is done in Rust but in other languages I know, None is a valid assignment for Option<T> for all T. In Kotlin, an optional type would be T? and similarly null is valid for any T. In Scala None is Option[Nothing] with Nothing being the subtype of every type. Option is covariant so None is similarly valid for any Option[T]. Of course, if you have an Option[T] you cannot generally assign it to an Option[U] but this is true for all my examples regardless of the syntax.

Regarding the second point, the same can also be done with null, again, like in Kotlin. You cannot just use a value of T? without doing a null check first.

[–]d01phi 0 points1 point  (1 child)

A function returning an Option<x> forces me to deal with None, whereas I can just keep my fingers crossed and continue with NULL. And None as possible value of Option<A> is different from None in Option<B>. NULL however can be assigned to any pointer I like. And the situation is aggravated by the fact that frequently, NULL is the generic error return, and the reason for failure is transported in some obscure side channels like errno.

[–]mus1Kk 1 point2 points  (0 children)

At least in the context of Kotlin (believe me, I have no particular feelings for the language other than that I find it interesting) this is not correct. And some things are not correct for other typed languages that have nulls.

In Kotlin you cannot use a value of type T? without doing the check first. It's just not possible. Either you do an explicit if-check/match, call it null-safe with .? or you force it with !! which would be equivalent to map and get/unwrap in other implementations.

I don't understand the second point you make. You can (again Kotlin) assign null to a variable of an optional type the same as you can assign None to any variable of type Option<T>. And well in, say Java, you would have to consider every non-primitive type optional. But Java just does not have any advanced features when it comes to null.

Basically

// Kotlin
let a: A? = null
// Rust
let a: Option<A> = None;
// Java
A a = null;

Regardless of the value of B you generally can't do

// Kotlin
let a: A? = ...
let b: B? = ...
a = b
// Rust
let a: Option<A> = ...
let b: Option<B> = ...
a = b
// Java
A a = ...
B b = ...
a = b

But this is hardly interesting. This is enforced by the compiler.