Post-1.0 Roadmap

I’ve posted a couple of possible use cases at Any thoughts on Ceylon-style union and intersection types? - #16 by Ladicek. I’d be interested to hear if they can be achieved in some othe way with the existing language (or with other future language changes).

Keep up the good work :slight_smile:

Can’t say I understand all the details, but it seems that most of the time a union type is replaceable with a sum type, which is a sealed class in Kotlin.

The JS use case is very valid, of course, but we are hesitant about this: complicating the type system so much is a big step that can’t be taken back later.

Union types can be useful instead of sum types if you want to have a union over types which already exist (e.g., String and Int) without having to declare new wrapper classes to be part of your sum. One might argue that you shoud declare wrappers, but using existing types directly is handy for prototyping.

Also, even without JavaScript, union types would be good for something like the Promise API I put up, where you want one of the types in the union to be a generic type parameter.

I’m curious what complications union types add to the type system – and I’m happy for you to point to me to some existing literature on that, but I wouldn’t really know where to start. As a user of such a hypothetical type, the one tricky thing I can think of is that, if you have several overloads which each use different but overlapping unions of types, there might not be a most-specific overload. I suppose you could try to catch that at compile time and forbid it, but that might be too restrictive, e.g., if you want several unions of interfaces. And there may be other complications for implementers of the type system, of course :wink:

I would absolutely love to see some support for typeclasses. Doesn’t need to be via the implicit functionality that scala uses, but perhaps a typeclass keyword that makes that typeclass available globally.

Overloads is one thing, then it’s simply not very intuitive for the user which members exist on a union type at all. Of course, one can call equals, but then can I call foo if it’s present in both types in the union? And what if it’s present, but takes slightly different parameters? There are very many questions like this, and it’s a big design effort to even survey the whole problem.

As of literature, sorry, I can’t point you at anything good for a start, but Google Scholar may be of help there.

2 Likes

Another useful addition would be guards in when’s, so I can write

when (any) {
is String if any.length > 10 → println(“long string”)
is String → println(“short string”)
else → println(“is not a string”)
}

I know we can call methods, but it saves having to create little functions outside.

A small one, but very useful for dsl’s is complete operator overloading. Both in terms of of currently missing overloads (such as boolean operators - && etc.), and in supporting specific overloading of operators such as < <= != etc without delegating to shared methods such as compareTo or equals. In addition, overloading in a specific class should be used even on primitives.

class X{
  operator Int.plus(r:Int):Expr {....} 
} 

fun X.bar = 5+4

Should be invoked when class X is in scope, especially in extension lambda’s as X.bar that should invoke the custom overload that allows for the encapsulated operator.

The only problem with allowing any operator to be overloaded is you end up with the Scalaz effect: scalaz operator cheat sheet · GitHub

I am not sure that absolutely all operators should be overloadable, but from my perspective the downside of people doing stupid stuff is overcome by the flexibility of genuine expressive power. More important in that sense is optionally splitting up the various comparisons.

it is completely insane :grin:

Certainly Scalaz is insane, and infix functions help reduce the need for operator overloading (somewhat, as they don’t do operator precedence), but for tricky dsl definitions, it is helpful if operator overloading is complete and sufficiently flexible. By all means, require that more specific comparison overloads must all be implemented (not only one), but without that the dsl (in this case typesafe SQL expressions) has to resort to eq, ne, lt, le, gt, ge… as well as additional parentheses.

Bad code, like Fortran, can be written in any language, but it doesn’t necessarily have to limit the ability to write good, clear code. Obviously language features need to be such that they are not hard to use correctly, and easy to use incorrectly, but the safety should not limit innate capability (by adding limitations to the implied semantics). By all means emit a warning if my comparison or boolean operator does not return a boolean value, but don’t stop me from doing so.

1 Like

I would like to chime in belatedly on this. I understand the reasoning behind limiting operator overloading, however, I definitely agree with the post that tying the comparison operators to compareTo makes it impossible to do an elegant implementation of SQL dsl or the kind of vector operations that are easily expressed in languages like R, Julia, etc. In R, for example, you can filter a table using:

table[colX > 3]

This kind of vector operation is really important to doing data analysis, which is an area where Java is very weak, and Kotlin could shine.

1 Like