Bike-shedding for my first post here, so here’s my 2 cents…
There once was a Scala compiler for .NET. It didn’t work, because targetting a new backend takes a lot of resources, and because the CLR is less flexible.
Turns out Java’s type erasure for generics is a boon for alternative languages. For example, Scala has higher-kinded types, but HKTs are currently not supported by the .NET runtime, and they need to be, as the .NET runtime does reification.
So on top of .NET, if you develop a language like Scala, you can choose to do your own type erasure, and thus have your generics not interoperate well with .NET’s generics. Also, you take a performance hit, because .NET’s runtime isn’t as smart as the JVM — I mean, the JVM may not do specialization for value types, but it tries doing escape analysis, and it has pretty good GCs lately. Without specialization of value types, you may end up with surprinsingly bad performance on top of .NET.
Or, you can go the way of F#, and introduce .NET’s generics with reification in the language, but then that pins you to what .NET supports. And Kotlin may not be as expressive as Scala, but I’m pretty sure it doesn’t want to be limited by what .NET supports, and we can probably spot differences in the current behaviour, at least when it comes to variance and such. Otherwise, for extra stuff, you introduce your own special DSL (e.g., F#'s inline functions), and hope that C# .NET doesn’t duplicate that functionality (e.g., interfaces with static abstract methods), as it will leave you in the awkward position of multiple ways of doing things.
Scala has a more expressive type system than Kotlin, of course. But it’s not the only sample in town. Clojure is another example whose .NET version did not go so well. Dynamic typing and the CLR don’t blend well, languages like Clojure taking a performance hit. That, plus there’s a complete lack of interest for alternative languages in the .NET community. At least, anything that doesn’t come from Microsoft has slim chances of generating interest.
It’s interesting that the JVM and JavaScript’s V8 (which is also inspired by the JVM), due to the immense pressure to keep backwards compatibility in the language, pushed most new development in the runtime, making it very flexible for alternative languages. Whereas .NET pushed most new improvements in the C# language. Although to be fair, with .NET Core, this seems to be changing, but even today, it’s being beaten by the pace at which the JVM evolves (e.g., Project Loom, or GraalVM).
In spite of the marketing, .NET has been historically a pretty bad target for alternative languages. It’s not just the capabilities of the runtime, but also small annoyances, like the debugging symbols being hard to generate and use. I remember that back in the day, at least, the files for debugging symbols (the .pdb files) where using a proprietary Windows-only format, so the Mono project had issues in using those files, generating its own symbols (.mdb files). At least they introduced “Portable PDB” in .NET Core, AFAIK.
The only somewhat successful alternatives to C# are F# and PowerShell, and these are still Microsoft languages. The JVM and JavaScript runtimes have fared much, much better, a lot of it boiling down to the Open Source culture around them.
For people wanting portability, a better choice for the future will be to go native, possibly with an interop layer for using .NET resources. This is because Kotlin, as a very client-side language, needs a good iOS story anyway, and if it can pull iOS off, then interoperating with .NET resources shouldn’t be a big issue. And going native doesn’t mean just Kotlin Native. It can also mean using GraalVM’s Native Image, which is pretty awesome, or WebAssembly, at least once languages with GCs receive good support.