Multiple types like in TypeScript

Kotlin allows multiple types already, when using null.

fun parseDigits(s: String):Integer? expects the programmer to test for null or an Integer type.

I suggest implementing an alternative syntax as soon as possible
(so later you can kill the meaningless questionmark)

fun parseDigits(s: String): Integer|null that results in an Integer or null value

So like in TypeScript multiple return types can be specified.

fun parseDigit(s: String): Integer|Float|BigSuperNumber|null

Consequently useable in all type definitions:

var list: List<Integer|String|null>() for a List containing Integer, Float or String elements only (so no need for casting elements in an object list).

fun something(cnt:Integer, arg:Float|Integer):Float|null

BONUS:

This will prevent the use of sealed classes in many cases.

IDEA:

Can be used for returning checked exceptions like:
fun parseNumber(s: String):Integer|Float|BadNumberFormatException

Has been discussed profusely here:

For what’s it worth, I really like the ? since something|null is where this is really useful

1 Like

If I think about union types, I think about a “union memory space” where the value is stored in a memory block with the size of the maximum sized element. Was that implemented C or even Pascal (?), I did not remember.

The Multiple types like in TypeScript are just generics helping writing cleaner code that was compiled to Object-Type. The compiler will force the programmer to check each instance of the returned MultiType. In combination with the fabulous smart casting this will create great code.

I will read your mentioned thread, thank you.

It might be useful for you to see how Rust has done unions as well; they’re quite similar to sealed classes and use exactly the method you describe in their implementation.

The types in typescript are just generics.

I did not like the creation of new objects just for holding object variants (Rust uses that mechanism too, I think). These holder objects are a large overhead (memory allocation etc.) and completely unnecessary then using Multi-Types like in typescript.

The typescript (transpiler) types are just used for forcing the programmer to test all declared instances (on return values), the code itself handles objects only (the javascript language itself is not changed).

So easy to implement and fully compatible to Java.

Future scala 3 version (dotty) also has union type.
Rust is effectivly different, as C union type, it’s just here to help to save memory.
In scala 3 or typescript, it helps to better model data & functions.

1 Like

So why did Kotlin not implement this.
In combination with when() and smart casts really beautiful constructions are possible.
Even an elegant way of checked exceptions.

fun loadString(f:File): String|IOException { ... }


val result:String|null=when(val s=loadString("/tmp/a.txt"))
 { is String -> s
   is IOException -> null
 }

or

val result:String=when(val s=loadString("/tmp/a.txt"))
 { is String -> s
    is IOException -> ""
 }

So why did Kotlin not implement this.

Because the designer don’t see the need for. Most people argue to use discriminated unions instead, which aren’t as elegant as non-disjoint untagged unions.

If i read it well, discriminated unions are “just” sealed type. So, you can’t have union type between unrelated classes (String|Int for example).
I don’t know if return String|IOException is a good example, a kind of scala try class should be better (if not already exist).

But a useful case for function could be on Iterator

interface Iterator<T> {
     fun next() : T|END_ITERATION
}

where END_ITERATION is a singleton object that indicate end is reached (instead of throwing NoSuchElementException exception)

@christophelswrote:

If i read it well, discriminated unions are “just” sealed type. So, you can’t have union type between unrelated classes ( String|Int for example).

Discriminated unions aka disjoint tagged union types aka enums can cope with unrelated types, but they require a nominal tag to disambiguate akin to Rust’s Enums.
The problem with this is, is nominal wrapping usually destroys subtyping relationships between union types and between union and other types.
Furthermore, unlike untagged non-disjoint union types (typescript unions), you can’t easily construct them by assignment, e.g.

enum{i:Int,j:Int}=2 //??? what does happen here.

Just to say, I’m not generally against non discriminated unions, indeed they make sense in certain situations like for enumerations.

Edit: It is theoretically possible to introduce subtyping for discriminated union types, but most languages simply won’t do it.

Theoretically, Kotlin and Java could allow for discriminated unions if they would permit for public constructors.

Wrapping the result into a sealed class just for differentiate different result objects is VERY ineffective. So the plain Object is returned and checked with isInstance. This is more effective, but very ugly.
The return type like in TypeScript is just the possibility to “hint” the compiler to expect type checks before using the result. The smartcase mechanism is aready present in kotlin (very great feature).
The code will look much cleaner and be still effective, because no wrapper classes are expected.