Implementation proposal for union types


#1

I’ve seen discussion here before about the lack of a union type in Kotlin. This is something that I found bit me in a project, but that I was able to work around. In the mean time I’ve come up with a potential way that union classes could be implemented

Syntax

The syntax of a union type would be A|B|C.... This syntax would only be valid where traditional types can exist (typealias right hand side, parameter type declarations, explicit type declaration). Such a type would guarantee that what you are given is one of A, B, or C.

Usage

fun hasUnion(u: String|Int): Int? {
    when(u) {
        is String -> u.toIntOrNull()?.plus(1) // The type here is smartcasted to String
        is Int -> u + 1 // Here it is smartcasted to Int
    }
}

Compilation

When the compiler encounters a union type, it will use the information about the types it is uniting to preserve type safety for the programmer. When generating code, the union will be erased, and replaced with Any. This allows union types of arbitrary arity.

Drawbacks

The main drawback of the approach I outlined above is that while Kotlin code will have the union enforced, interoperating Java or JavaScript will be free to provide any value. I cannot think of a way to mitigate that at this time.

I’d appreciate any feedback on this idea, and perhaps help refining it into a KEEP if that would be appropriate.


#2

That’s arguably one of the most critical parts in the proposal. When you want to create a keep you should provide some solution for this, even if it’s not an ideal solution. It can still be refined later.

I see this problem very similar to how the Kotlin cannot enforce Java code from passing null to nonnull arguments. And it can probably be solved in the same way Kotlin solves the nonnull problem: by adding runtime checks at the beginning of every function that takes union type arguments.


#3

How did you work around this?

I also need this sometimes. I always create a sealed class with as many sub classes as I have types for one union. Then these classes wrap the corresponding native Kotlin classes or others.

Example:

A function needs to return either an ErrorEnum (my enum) or a List<String>. The sealed class would be ReturnValue (which is either a subclass of something else or the class itself is better named to avoid confusion). The sub classes of ReturnValue are then Error or Success, which both wrap their corrsponding values: Error -> ErrorEnum and Success -> List.


#4

Any time that I’ve needed this I’ve needed to come up with a solution composing enums and sealed classes and I’m never really happy with how much I need to build up around this to represent this sort of operation. That’s why I’d like to refine this into a KEEP to properly represent this construct


#5

The keep should show possible workarounds that are currently possible.


#6

From the example you just need overloading with dynamic dispatch. I would prefer the compiler to generate a dynamic dispatch when needed and keep the language clean (just an extra contextual keyword). I’m not saying that dynamic dispatch should be implemented, but I think it is a better solution than the sample code.


#7

Indeed dynamic dispatch would be a cleaner solution for the provided example. Maybe the op can provide a better use case for union types.