This must have been discussed in the past, but I was not able to find anything using the search.
This message is about generic functions. I’ll be using terminology from CLOS (Common Lisp Object System), and in discussing this with people who are not familiar with CLOS, I have noticed that the terminology can be confusing, so please keep that in mind while reading this in order to avoid any confusion.
I would really like to see an implementation of CLOS-style generic functions in Kotlin. I’ll explain the behaviour using Kotlin-style syntax, so that I don’t need to explain the Lisp syntax to any readers not familiar with Lisp. In CLOS, methods do not belong to classes. Instead, we have generic functions. A generic function is a function with multiple implementations, which are chosen at runtime based some some set of criteria.
Let’s say we have a function collide
that processes collisions between two objects in a game:
generic fun collide(obj1: Any, obj2: Any)
We can now implement multiple implementations of this function (CLOS refers to the concrete implementations of a generic function “method” but this terminology would be very confusing in Kotlin):
class GameObject
class Ship : GameObject
class Planet : GameObject
impl fun collide(obj1: GameObject, obj2: GameObject) {
// this will process a collision between any objects unless they are handled by a
// more specific implementation
}
impl fun collide(obj1: Ship, obj2: Ship) {
// process ship-to-ship collisions
}
impl fun collide(obj1: Planet, obj2: GameObject) {
// All planet collisions are handled here
}
impl fun collide(obj1: GameObject, obj2: Planet) {
collide(obj2, obj1)
}
In CLOS, what would be a “normal” method in a language like Kotlin is simply implemented by convention as a generic function with a single discriminating argument. In other words, CharSequence.charAt()
would be equivalent to a generic function with two arguments:
generic fun charAt(seq: CharSequence, index: Int)
impl fun charAt(seq: String, index: Int) {
// implementation here
}
In CLOS, the method by which the implementation to be called is chosen can be configured using what is called “method combinators” which are fully customisable by the developer. The entire system is incredibly flexible, and if anyone is interested in learning how it works in Lisp there is some information on Wikipedia about it: Common Lisp Object System - Wikipedia. For some more in-depth discussion, here’s another article: Fundamentals of CLOS
I believe that the first reason to not implement this will be the argument that method resolution for generic functions will be very slow in Kotlin. That is true. It will be significantly slower than normal method calls. This is actually an issue in Lisp too, and lots of effort has gone into making the implementations as fast as possible. Typically the compilers implement a method cache that caches the results of method lookups in order to avoid calling the method combinator for every call.
Of course, even with such optimisations it’s still slower than normal method calls, but in Lisp we still see very heavy use of generic functions, and in practice it works really well. You might want to avoid using them in very tight performance critical code, but that’s a very small portion of an application.
Note that ABCL, which is one popular implementation of Common Lisp runs on the JVM, and it seems to work pretty well.