So. What's next in language design?


#1

Kotlin had a great 1.1 release and a great year+3 months.

I’m now starting to wonder what ideas are being cooked up for the 1.2 release of the language (as opposed to tooling, where there is already some clarity). Yes yes, I know, nobody likes talking roadmaps, but in the absence of roadmaps people will just lobby for their own ideas :slight_smile:

There seem to be at least four candidates:

  • Completion and integration of the serialisation feature.
  • Integration of the immutable collections library into the stdlib.
  • Promotion of coroutines from experimental to a core part of the language.
  • Support for value types when Java eventually supports them. I’m assuming this is a “no-brainer”.

Beyond that little has been communicated about future plans.

Something I wonder about the overall strategy - maybe there won’t be many more features at this point. Kotlin has focused on combining the best ideas from other languages rather than researching new ideas, which means that eventually it’s going to run out of things that have already been tried out in other languages. At that point it can either go the Java route and consider the language done (with the focus being almost entirely on tools), or it can start to incorporate more experimental ideas.

My suggestions:

  • Support for @CheckResult (KT-12719)
  • Some new attempt at checked exceptions. Java didn’t get them quite right and the stdlib definitely didn’t get them right, but knowing how your code can fail at runtime when writing the code is just valuable for writing reliable software. Perhaps some kind of awesome IDE integration can help here (it doesn’t have to be a language feature, I guess).
  • More powerful inlining:
    • Being able to statically work with reified type parameters in more ways (e.g. if (T is Foo))
    • Syntax sugar to avoid one-liner inlined functions that just forward to a KClass/Class parameter using reflection.
    • Flow typing that flows through inlining. Could unlock some nice new language / DSL constructions based on changing the type of things at compile time.
  • Exhaustive when
  • Compiler to understand thread safety/lack of thread safety annotations so that, for instance, you don’t need to copy a var onto a val on the stack in order for smart casting to work. Currently the compiler is conservative with smart casting because it assumes all object fields can be written to concurrently, but often (e.g. in ui code) this is not an appropriate default.

I like the idea of a more powerful type system in general, but I’ve learned by now that suggestions of that form tend to get shot down on the grounds of being incompatible with Java, not working well with incremental compilation and so on :frowning:


#2

Kotlin 1.2 will be a fairly short release, aligned with Java 9, so the only thing from your list that is very likely to be there is the removal of experimental status from coroutines. We plan to publish a blog post with some more details this week.


#3

I’ve been hard at work writing a write-once-deploy-everywhere game/application framework. The biggest areas I see needing work are the places 1.1 started towards and some of the sneak peaks I’ve been seeing got me really excited. Things like common modules, Further bettering the JS side, LLVM deploy, etc.

Additionally, I’d love to see:

  • JS - Lower filesize (work with advanced mode for closure compiler)
  • JS - Faster performance (a release flag for eliminating runtime typechecks)
  • JS - A ready-to-go template when creating a new JS project.
  • General - Kotlin utility methods often cause allocation, making Kotlin a cumbersome choice for game development. (List iterators, range instantiation, etc)

#4

Well, other than the last those aren’t really language features. They’re tooling features. I’d like this thread to stick to language/type system design.

Kotlin is similar to Java in that it lacks value types and relies heavily on the compiler to optimise out unnecessary allocations. HotSpot is decent at that, Graal is a lot better, but if you want to use the LLVM backend then you’ll be relying on the LLVM escape analysis or manual stack allocation markers. I don’t know how good LLVM is at eliminating short lived allocations. Managed and unmanaged languages are different enough that I worry Kotlin/JVM and Kotlin/Native may diverge quite significantly over the long run … we can see that starting already with their different approaches to threading. But I’ve banged on about that plenty before, I won’t do it again here.

Let’s talk language features. Type classes, better generics, etc.


#5

I don’t know whether you’ve seen it already but Andrey Breslav gave a talk here about a week ago on the future of Kotlin though I think the things he was talking about will be for versions beyond 1.2. His answers to some of the questions asked were also quite illuminating.

If you haven’t time to view the video just now, there’s a SlideShare presentation here.


#6

It’s also now been announced that JSR 376, the proposal for the Java Platform Module system, has not been approved by 13 votes to 10 (see here). I suspected that this might be the outcome when I read a few days back that both IBM and Red Hat were not happy with the proposal as it stands and were going to vote against it.

Consequently Java 9 (and possibly therefore Kotlin 1.2) may be somewhat thinner now than previously anticipated as it is difficult to see how this can be sorted out within 30 days.


#7

I would love some macro, at least for conditional compilation, but everybody else hates that.
BTW js(’’) is somewhat a macro with gwt js parser that returns js ast from string while compile kotlin src. so… why not kotlin? It is basically one step from D macros. cmn guys :slight_smile:

Some way to tell compiler to stop inlining in some fun. Try decompiling any string operation heavy func with lot of trims and other methods. Last time i checked if you call trim 5 times in fun it inlines trim body 5 times. Great for code size :smiley: Maybe they have fixed heuristics, did not checked.

More operators like for DSLs? Right now you can use unary plus/minus in typesafe builders which is definitely not enough. I know there was discussion about double extension function and that they are really complicated. So introducing way for more unary funs/operators seems like good enough compromise. The only option now are functions but they require either () or {} wich is not clean enough for dsl.

Cleaner syntax for capturing vals from typesafe builders. Right now you have to use nullable/lateinit var if you want to have ref of some element inside builder. This is tricky one, but still can be solved manually by wrapping builder/result. Maybe some automation for this specific case. Bonus points for functional solution. I don’t want my dsl be class wrapped. Anonymous data classes can help…

Anonymous data classes :smiley: Like csharp but BETTER. because of nullability and vals. Also having anonymous entities in language really helps writing macros… wait. I can do dsl stuff with zero overhead and in external lib if only there were macros. :innocent:

Also processed strings/tagged template literals. Which are basically macros… sigh. :sweat:


#8

I remenber a pool of some months ago. Was the results published?


#9

Yes, you’re right, there was a poll a while back on proposed future features.

I can’t find the link now but, from what I can remember, there was a list of 20 features which the Kotlin team considered they might be able to implement (which was interesting in itself!) and you were asked to vote for your 3 favorites from this list to help them to prioritize future work.

I think it did say that the results would be published but, if they have been, I can’t find any link.

One of my personal favorites from the poll was the ability to easily build formatting information into string interpolation. Currently, you can do this using the String.format function but this is rather messy - and difficult to read - if you have several fields to format in the same string.


#10

On a language level what I’d most like to have is “free” abstractions. Some of that will be in the form of adding more optimizations to the compiler (I know the Java compiler doesn’t optimize either, but Java to JVM bytecode is almost 1 to 1). Kotlin adds a number of cool capabilities that could really benefit from things like reliable dead code elimination and constant propagation. Inlineonly would be nice to have outside of the standard library. This is in particular important in Android and other situations where method count matters.

Some enrichment of the type system would be worthwhile. A self type could be useful as well as standard ways to ensure that a method calls a parent etc. A way to access the variable considered in a when statement.


#11

It is called zero overhead abstractions and is philosophy of c++. Unfortunately it is very hard to introduce them in language. Most of this features in c++ are heavily dependent on template specialization and macros.
Hard inlining is not zero overhead. There is space overhead and architectural/programmer mind overhead. Zero overhead features are more transparent and automatic.
Example of zero overhead in c++ could be std:begin/std:end based interfaces. Which was sugared into for/auto loops in recent versions.


#12

I would like to see this too, and I would like 3 variants:

  • Must invoke super: compiler error if the developer doesn’t invoke super
  • Invoke super first: automatically generated (and compile error if the developer invokes super explicitly?)
  • Invoke super last: automatically generated (and compile error if the developer invokes super explicitly?)

I don’t see problems with compatibility when you change the specification and you run with sub classes that were compiled against other behavior: The code will run. It may not behave as you want it to, but this can happen with many other language constructs too.


#13

I am very aware of how C++ does this. C++ metaprogramming absolutely requires the help of the compiler, but even in Kotlin some aspects beg to be made “zero-overhead”. An example would be delegate properties in those cases where the delegate has no state other than a reference to the owner class.


#14

I would like some relatively simple QoL improvements for libraries. First: I miss configurable string interpolation. I want to write a wrapper around JDBC, but without proper string interpolation it doesn’t make sense. Formatting should be built-in too, using String.format isn’t good solution. So something like "{a.b:.2f}, where part before `:` is kotlin value and part after `:` is passed to some function which will perform an actual formatting (for SQL it could be `{s:varchar}`, for example, because JDBC sometimes requires actual DB type with parameter value).

Another improvement would be something like __FILE__, __LINE__, __function__ in C. Some kind of compiler magical value: fun logDebug(message: String, file: String = currentFile, line: Int = currentLine). Compiler has this information when it compiles file and it’ll be inlined with 0-cost. Retrieving this information in run-time using stack trace is slow and discouraged in release builds. So you can’t have this information in your production logs now, but you could have it with this feature.

And, yes, using nullable private var now isn’t nice. Something should be done about it. I often encounter this situation when I have to create a local variable just to work with this value. It looks like bad code. May be just allow it for private fields and add something like volatile to explicitly mark field as unsafe. Compiler could introduce local variable under the hood, if I use this field, that’s OK for me, I just don’t want to see it in my code.


#15

I just remember another set of things that I’d like to have in the language (I’m not sure how feasible it is in conjunction with Java proper though). Both are to do with generics:

  • In many cases where generics come into play there are a number of related classes that all need generic parameters. This often means that to correctly specify types you have 3 or 4 related generic parameters that have references to each other. This is ugly, can slow the compiler down a lot, and in general tedious. To solve this it may be an idea to allow referencing the generic type parameter of (an instance of) a class by name.
  • Sometimes it is necessary for a function that doesn’t care about the generic parameter of a type (it’s interface does not and should not have a generic parameter) to still know it anyway, for example to allow the compiler to know that it will return the same type as it got in. This can be handled using a helper function that does have the type parameter, but it would be nice to be able to actually define a generic type value in the function body (rather than in the declaration) that is the type of the actual value (of course with the semantics equivalent to the helper function, but without the overhead of having a separate function with only the purpose to provide the generic parameter).
  • Perhaps more of a stretch, but it would be really nice to have a generic varargs parameter where it is possible to have the type of each individual argument. I could see a case where this would only be available in inline functions though.

A lot of this functionality is inspired by my kotlinsql library that basically provides a DSL and associated functions to do typesafe sql definitions and queries. As an example of where the associated classes come in, there are columns (of a type), column types (with associated Java type). One of the needed features for type safety is to be able to have the compiler know the associated java type for a specific column. This is purely determined by the column type, but requires the column to have generic parameters for both the column type as well as the java type.

Another problem in this library is that it supports iterating over the results of a select statement using a lambda statement with a parameter per resulting column (of course type safe). This still must be true in case of modifiers such as a where statement (which creates a new, more complicated, query object). This involves a lot of passing generic parameters around, in this case actually 3 parameters per column. The current solution is to actually generate the classes (extremely repetitive, without interesting code) for all cases between 1 and 10 columns (the count is actually just a constant in the generator), but for the 10 column case this means classes and functions with 30 generic parameters.

The one salvation is that when actually using the library, you don’t specify any generic parameters at all. It just works, and is type safe. The library even allows for custom column types that can do translation between more complex types (url, UUID, anything else) and the natural mappings of their underlying columns :slight_smile: . Btw. the delegate property overriding mechanism in 1.1 allows for a real cool feature to have the name of a column be (optionally) taken from it’s name in code (rather than specified as a string parameter).


#16

I’ll voice up for typeclasses again. Very useful, doesn’t need to be implemented using via implicits like Scala, as implicits is a big no no, could be a Haskell style single typeclass, or via an import, or …


#17

I know pattern matching was long ago deemed unnecessary or too complicated, but that’s the last major language feature I feel is missing.


#18

Pattern matching is the other one. Typeclasses and pattern matching. Although I’d probably take the latter and live without the former.

And it could be done in an easy to understand way I think - although I don’t know how hard it is to implement behind the scenes.

when (person) {
  Person(name, 100) -> println("My name is $name and I have received a telegram from the Queen!")
  Person(name, age) -> println("I am $name and I am $age years old")
  other -> println("I am not a person. Soylent Green is people!")
}

#19

Please add Pattern Matching. This is one big feature I miss in Kotlin and show the true power of Data Class.


#20

I had a silly bug the other day that would be elegantly solved using a “strict” typealias; let’s say I had a strict typealias Cm = Int, the compiler would either warn or prevent me from doing val x: Cm = 10; val y: Km = x without an explicit cast.

It would prevent mixing various units by mistake, referencing database tables by using the ‘wrong’ ID etc…