Kotlin 1.0 Released: Pragmatic Language for JVM and Android
blog.jetbrains.com
blog.jetbrains.com
It's a much needed upgrade to Java whose fundamental advantage is what I'd call "crispness": you can write terse code that is actually understandable. In particular, the notion of inlining closures allows for efficient functional programming and great DSLs.
In comparison to Scala, Kotlin is much less advanced, but much much simpler. Like said earlier, it's easily as terse as Scala. Scala, however, is much more expressive (what I'd give for typeclasses...) and that sometimes hurts in Kotlin as it does in Java.
There's also Ceylon (JVM language by Red Hat) who is seemingly in the same niche and which I very much wants to experiment with. From what I've read so far, Ceylon seems conceptually more elegant and more powerful than Kotlin. The open question is whether it can match Kotlin's crispness.
It feels a bit early for a release however. The compiler is still completely wonky. Inference sometimes fails on things that should be completely trivial. It is particularly bad at taking return types into account. It's fully usable, but expect to work your way about some linguistic unpleasantness (or outright bugs).
Also, for some reason, Kotlin seems big in Japan.
I don't personally understand why people care about that: a simple structural match on names/parameter types doesn't seem like a strong enough signal that someone actually intended to implement such an interface.
But ... type classes can be implemented on the JVM relatively efficiently these days. I threw together a simple Kotlin version a few weeks ago that let you write something like:
val foo = Foo()
val bar = foo.dynamicCast<Bar>()
where Bar was an interface that Foo could have implemented but didn't. It worked, but I didn't see any value in it for myself, and it didn't use the latest JVM reflection APIs so it was slower than a normal interface call.If there's some big population of people who really want this in Kotlin and would be much happier if they had it, then I might sit down and reimplement it on top of Remi Forax's proxy2 framework, which should give 1:1 native performance.
Typeclasses are more powerful than structural types in that the implementation doesn't have to be there already (i.e. you can have an automatically resolved adapter). You might be able to hack an implementation by combining extension methods with structural types (though given the internals of how extension methods are usually implemented on the JVM I doubt it). They also allow a way to do "static virtual methods" i.e. polymorphic methods that you don't need an instance to invoke, which are used for things like Monoid#zero. Finally they provide a nicer way to express F-bounded polymorphism (i.e. the constraint on Enum or Comparable that we try to express in Java as Enum<E extends Enum<E>> or Comparable<T extends Comparable<T>>, which is clunky and doesn't actually fully constrain implementations the way it's intended to).
The best explanation of Scala type classes IMO is here:
http://danielwestheide.com/blog/2013/02/06/the-neophytes-guide-to-scala-part-12-type-classes.html
But as you say, what Scala calls type classes are more of a pattern with some language support - you just define what is essentially an adapter, but one which is constructed/used implicitly.Being able to write:
interface Foo {
val prop: String
fun someMethod() = "we are set to $prop"
}
and then being able to treat any Bar as Foo as long as it has some property "prop" but without regard for whether it has someMethod, sounds pretty much the same thing as Haskell/Scala's idea of type classes to me. implicit object IntMonoid extends Monoid[Int] {
def zero = 0
def plus(a: Int, b: Int) = a + b
}
implicit def endoMonoid[A] = new Monoid[A => A] {
def zero = identity
def plus(a: A => A, b: A => A) = a andThen b
}
You can't do that with a default method.I really wonder if people would have different reactions if this was presented as "Addable" instead.
Integers can also be combined with other operations, like multiplication, min or max. None of those sound like "adding" or "merging" them to me.
That's reverse API psychology for you :D
Oh, I don't know about that one. In Scala type-classes are interfaces and you can put that OOP to good use. For example in the Cats library, you get an Applicative type-class inheriting from Apply, a FlatMap inheriting from Apply and a Monad inheriting from FlatMap and Applicative. By comparison in Haskell a Monad is not automatically an Applicative and you get duplicate functions with different names, duplicate code, duplicate tests, etc.
Another advantage is that in Scala type-class instances are actual values that you can pass around. And because type-class instances are actual values, along with implicits which are lexically scoped, you can always provide another instance of a type-class, depending on context. You do not have the modularity problems arising from usage of type-classes in Haskell.
"The best implementation" is entirely subjective IMHO.
EDIT: got down-votes, not complaining, but I'd like to know where I'm mistaken. Thanks.
pure = return
(<*>) = liftM2 ($)
In Scala, you get it for free for any monad.
The short but slightly inaccurate description is that type classes are a way to allow interfaces to be implemented separately from the way you define types.
What I mean is what the Scala doc calls "context bounds": http://docs.scala-lang.org/tutorials/FAQ/context-and-view-bo... (bit outdated: view bounds are deprecated)
You could think of it as "abstracting over the adapter pattern", i.e. being able to write methods that take as parameter things that can be adapted to an interface.
(And of course the main point of the Adapter pattern is retroactive implementation of interfaces.)
But, and this is important, you get to pass the actual object instead of the adapter. This matters when you need to required multiple adapters, or when you work with object equality. The object type can also flow through the type parameters (although that's sort of doable with adapters only).
Finally, because a typeclass instance is actually a single object per type satisfying an interface, you can use it for things like factory methods (what they call constructor classes in Haskell).
It's close to what I propose here: https://discuss.kotlinlang.org/t/kotlin-and-the-expression-p...
but there are differences. Scala typeclasses are based on implicits, this gives you more control to select the adapter you want depending on where the code runs. But, unlike what I propose, it's dependent on static typing, so it's not possible to select the adapter depending on the run-time type.
I do understand Haskell type classes but in a language like that, they're almost obligatory as it's not an OO language at all. But in something like Kotlin, you should need it far more rarely, so the automatic/implicit "conversion" (I realise it's not quite "conversion" in Haskell) is less vital. So some convenience for creation of adapters would seem to get you the same practical benefits in much the same way.
With respect to the difference between using a singleton vs a freshly allocated wrapper - is it really worth complicating the Java interop for what seems like an optimisation? Code that works with object identity is rare in all the code I've written and can't even be done at all in a pure functional language. And requiring multiple 'adapters', you mean interfaces I think (?) can be done already by imposing multiple generic type constraints (the where clause). The VM should be able to optimise out the overhead of the wrapper class in many cases.
That leaves implicit conversion. But Kotlin doesn't even auto-convert Int to Long. I doubt that implicit conversion will ever be a part of the language outside of a few special cases like toString().
I guess for my use cases, a wrapper class + an extension method on the existing type that adds a toFoo() is sufficient.
This however, is not my main concern, and as you remarked, it does not fit very well in OO, which is why it isn't in my proposal.
On to the interesting part.
You want multiple adapters, rather than multiple interfaces, precisely because you want retroactive interface implementation. That's really the crux here. So either you pass two adapters, or you make a class combining the two adapters. I think both solution create too much bloat to be acceptable.
For me, equality isn't so rare. Both identity (==) and structural equality (equals(), done right) pop up with some regularity. Each time you use a map, for instance. And functional languages are perfectly capable of structural equality.
While Scala typeclases are implemented with implicit objects, typeclasses precisely avoid using implicit conversions. You still have the original object, the typeclasses just provides functions to work with it. It's only an implicit conversion in the same sense that passing an ArrayList to a function expecting a List is implicit conversion. This is manifest in Scala where you require a typeclass using some sort of type bound.
WRT a combined adapter class, it'd look like this in Kotlin:
class FooBarAdapter(val x: Baz, private val y = Foo(x), private val z = Bar(x)) : Foo by y, Bar by z
A bit bloaty, but still a single line of code.(I know you already know this, I'm writing it here for Kotlin newbies).
It's very nice, though I can see that becoming quite heavyweight when you nest these calls (either heavy from the overhead, or from the syntax - at least you get to decide).
Also, type-classes in Scala are not implemented by means of "implicit conversions". It has nothing to do with it. Type-classes in Scala are implemented by means of implicit parameters, also called context bounds, but that's different.
Of course, you don't really need implicit parameters to work with type-classes. That's just syntactic sugar, because you can just pass dictionaries (type-class instances) around manually.
No, the far bigger problem that Kotlin has is that it doesn't support higher-kinded types. Without higher-kinded types you can't express generic code for containers (e.g. M[T]). And without that, you can't do do type-classes and you can kiss most FP abstractions goodbye.
If you don't believe me, try coming up with an interface for things that have "map", "filter" or "flatMap.
What an awesome term, I love it.
The interoperability and backward compatibility is one of the most useful features, meaning I don't have to wait for an entire ecosystem to develop around it.
The tooling is also incredible and will only get better.
Concurrency support with Quasar and Comsat makes it relatively effortless without reinventing new libraries.
Overall, Kotlin, IMHO, is one of those items in the "Pro"s section on any shop seriously evaluating a JVM stack. Especially with Java9 on its way, it's only going to get better.
What's Java 9 going to add that Kotlin can benefit from?
The real juice will be in Java 10, assuming value types, proper generics, JNI replacement and AOT support get delivered.
Here's a little starter project for Kotlin webapps using Spring Boot and React.js, I made a while ago:
I want to cry.
http://macstrac.blogspot.co.uk/2009/04/scala-as-long-term-re...
https://github.com/mythz/kotlin-linq-examples
Thanks to JetBrain's tooling prowess it also has great integration with Android Studio - Light years better for functional programming than Java 1.7:
https://github.com/mythz/java-linq-examples
Which can seamlessly integrates with Java within the same project. Google/Java/Android Devs are sitting on a unrealized gold-mine of productivity with Kotlin, should be the modern successor for Android.
LINQ is not (just) an interface over some collections. It is a comprehension syntax (that kotlin doesn't have) that works on any types that have Select/SelectMany/Where.
This gives you a lot more power than what Kotlin gives you, as you can 'extend' other things that don't inherit from an IEnumerable<T>; for instance Task or a custom data type that has its own inheritance hierarchy.
The 'comprehension' part means you can deeply nest things (similar to scala's for compehension) without building up a stack of {}. This is quite common when doing asynchronous programming, and Kotlin doesn't really help you out at all here.
LINQ also allows capturing Expression Trees however that's only useful for developers creating LINQ providers or where they need to capture and rewrite/reproject the expression. None of these examples are in the published LINQ 101 examples as they're usefulness is limited to a small niche of use-cases.
I'd love to see how you think the kotlin (or java) version of the following would be more 'readable'
var result =
from a in taska
from b in taskb
from c in taskc(b) where c.Contains("blah")
select c + a;Whilst the code is terse it's also deceptive as it's not obvious it's performing a cross-join over multiple collections - so even in this case I'd prefer using a nested maps which IMO is more readable as it's clearer what's actually happening. Readability != terseness, it's clarity of intent.
It has nothing to do with SQL cross joins across multiple collections.
LINQ is useful for things that aren't even collections! In my example I was using them on a Task<T> to represent an asynchronous computation. This is common code we would write in a large C# application I was on at my previous job.
I've been doing C# for years, use and enjoy Linq, and I can't remember the last time I touched the comprehension syntax. IIRC, it does make a few relatively obscure things easier than they would be with method and lambda syntax, while there are a few different obscure things that are easier without it. On balance, I lean against it as being yet another sub-language for you and the rest of your team to learn and understand what it's really doing under the hood.
If they are enumerable, then what exactly does c + a do? It is creating a new enuerable of every a added to every c, like a SQL cross join?
If it is a single object, then what exactly does the where clause do? What does it return if c does not contain blah, and what becomes of a in that case? Does it get returned, added to null, disappear into the ether, or does it figure out that it doesn't need it, and never actually evaluate taska? What happens if taska modified some other state somewhere? For that matter, if it's figuring that out, that would control the order that the tasks are evaluated in. What is that, exactly? Is taska run at the same time as taskb, or does it wait to see if there is actually a c to add to it's result? Or maybe it gets evaluated before or after taskb, or at the same time.
That stuff matters. I'd rather make it clear at a glance what's going on than write something super short and clever that nobody can figure out the details of.
As far as I know, LINQ to SQL uses Expression Trees under the hood, I'd argue that even if you're not using them directly, LINQ to SQL is a pretty useful application of LINQ.
Effectively, Expression Trees are what enables custom macros for LINQ. Even if not everyone writes macros, the number of users of those macros is much more widespread.
[1] https://spring.io/blog/2016/02/15/developing-spring-boot-app...
> I still missed something on top of Executors and CompletionStage.
You could use RxKotlin. It has Schedulers and Observables/Single/Completable for interacting with async tasks. https://github.com/ReactiveX/RxKotlin - I dislike the DSL // not a really useful criticism I know.
- Some things needs Groovy which isn't a mainstream language
- Had Bad support for Scala, which I use heavily mostly resolved since 2.2 and thanks to linkedin newer version even have twirl + playframework support.
- sometimes the syntax file of the DSL couldn't be highlighted in eclipse / intellij
- Bigger build files could be really slow (mostly resolved in newer versions)
Btw. whats really really good on Gradle is the dependency resolution, which is really fast compared to something like sbt.Originally on Gradle.org's website, they wrote they'd encourage anyone who wanted to enable Gradle to allow another scripting language for writing build files, and that they'd help them with bundling it. But since Gradle 2, it looks like too much of Gradle's own source and plugin code is written in Groovy for that to still be feasible. Gradleware also employed one of the former developers who used to work on Groovy when they were retrenched by VMWare a year ago, and I suspect he'd actually sabotage any outside attempt to make Gradle polyglot, like, say, Vert.x is. So it looks like you're stuck with Groovy if you want to use Gradle. With the good comes the bad, as they say!
(And because its "config" files are arbitrary turing-complete code, its IDE integration is never going to be as good as Maven's)
And as in all things if your code gets too complicated you need to refactor, extract logic into methods/classes, etc.
I do have a problem with Gradle which is that it is almost entirely magical unless you are a pretty advanced Groovy programmer to understand how it is doing what it is doing. I have never felt more disoriented than when trying to learn how to customise a simple aspect of my build and having people post snippets that work but seem completely disconnected from anything else in the build process.
And maybe this is making a virtue of necessity, but I find the overhead of creating a plugin stops people from putting random "different compiler arguments on a Wednesday" conditionals in every build, which is all too common if you give them immediate access to a turing-complete language.
Your criticism applies equally to non-build code. Don't approve PRs for bad/inscrutable code.
In gradle you can build plugins just like maven. But a simple 'if then' doesn't require all the heavy lifting of a plugin.
But code is where logic goes. People expect logic there. It's the same objection as to logic/conditionals/looping in web templates, or routing config files, or persistence object mappers. If it's logic, it belongs in code!
Gradle's biggest problem is that it's too flexible.
- It is the slowest of all JVM build tools available.
- Hard to know what DSL should look like in each version.
- Seems like the only thing keeping Groovy afloat
- slow resolution (could be really really really slow)
- transitive dependencies could be a mess since it could actually will mostly give you a evicition but it's hard to actually resolve that.
- however if you have two plugins a evicition is not printed, so you will actually get a funny stack trace if you actually use a library that resolve something via reflections. (i.e. closure compiler)
- sbt is slow
- sbt has some wierd operators.
- has problem with dynamic ranged versions
- scoped settings are cool, but could be wierd to understand, especially for new people
- sbt-web which tries to use npm which it packages as webjars which actually is a total mess since you actually run into the great world of npm packaging and try to force it into a completly different format.
there are more, however these are the ones I often deal withWhat you're asking for is Turing-complete XML.
History has shown that this is usually a mistake.
https://github.com/jruby/jruby/blob/8e29ae1302e7aa989b8808f7...
Well yes they could be - but this isnt a project to build a general purpose build tool so this is not really relevant
fun lock(lock: Lock, body: () -> T): T {
}
Is there any technical reasons why normal function declarations are not using '->' as a return operator ?
fun hello(name: String) -> String {
println(name)
}Swift [2], Rust & others are using it.
It will just make the experience uniform with consistent Functional Types
def lock(...): T = ...
val lockResult = lock(...): T
In Scala at least "=> T" is the syntax for a lazy T (i.e. a function of no arguments that returns T), and so your "hello" reads like a function that returns a lazy String.I see with Project Rider, JetBrains clear strategy to support major platforms - JVM, Native, Web, CLR.
Is there a CLR backend planned for Kotlin ?
Congrats though well done on release.
It's almost like Kotlin was designed for Android to bring new life to its ancient Java version.
This could also mean you've designed a verbose and turgid language. A better metric might be # of projects using Kotlin. (For the record, I'm actually really excited about using Kotlin, I just found it somewhat hilarious to boast about exponential growth of LOC.)