Implicit interfaces as in Go


#1

I like the idea of interfaces in Go where a class does not need to implement an interfaces explicitly, see A Tour of Go. A class imclicitly implements an interface if it defines the same methods as defined in the interface. This really adds a lot of flexibility and makes you more productive. Today I looked at I wished Kotlin would support that feature. Any plans?

– Oliver


For-each on Iterator?
Implicit interfaces
#2

Could this be implemented via a library API? It couldn't be 'implicit' I suppose, but it would be possible to have a call that took an interface, checked that the object implemented the same methods that the interface provided, and then returned a Proxy that implements the requested interface that passes the calls through the the implementation object. Would that come close enough?


#3

I'm struggling to find the advantages of structural typing over nominal one. One less class name to write, maybe? And the cost is very high: it becomes impossible to easily find out which types are applicable in which place (even compilers and IDE's will struggle with that), add a function to your type and suddenly, you implement a whole set of new types you had no idea about (did you really mean it?), etc...


#4

I agree that never explicitly declaring a class to implement some interface as in Go seems to be an odd idea. The information for the developer that some class explicitly implements some interface is very valuable and the code becomes more self-explanatory this way. But there are cases where you run into trouble and implicit interfaces can come to your rescue. For example, class Map and List/Set/Bag/etc. in Java do not have the same super class. When you extend Map and maybe List you have to add the equivalent extension methods both to Map and to Collection. Okay, that Map and subclasses of Collection do not have a common superclasses is a design mistake by the Sun guys at that time. But you cannot change it and that is also true for many other code in foreign jars.

Scala would solve this probleme with an implicit conversion. It has great value for that kind of practical problems, but with the number of implicit conversions rising you loose oversight over all those implicits and after a while it gets hard to predict what your code will do after a change. Not so with implicit interfaces. If implicit interfaces are only applied when there is no other way for practical reasons, it can come to your rescue and you can continue with some clean design instead of continuing with someone’s else blunder as with Map and Collection.

– Oliver


#5

We like this idea too. Unfortunately, it can't be efficiently implemented over JVM under 7, and from 7 and later it requires heavy use of invokedynamic which is likely to compromise performance.


#6

re : and from 7 and later it requires heavy use of invokedynamic which is likely to compromise performance.

Do you have any details on why invokedynamic would probably compromise performance? Or is it just a hunch?


#7

invokedynamic is obviously slower than invokevirtual/invokeinterface. Call it a hunch, if you like. The thing is that we'd have to call almost _everything_ through invokedynamic.


#8

That makes sense.  It would be an interesting one to measure.  

From what I understand of HotSpot, it would definately be slower during the “warm up” phase.  However there are optimisations within HotSpot to optimise out the calls to InvokeDynamic after a result has been calculated for a callsite.  Thus promising to be faster and more inlinable than the pre JDK7 approaches where the dynamic work had to be performed everytime and was usually far too large for Hotspot to inline effectively.  How effective this is in practice? Who knows?  It would be good to get some numbers on it.  


#9

Not everyone is a fan of this feature of Go.  To quote from this article:

"But when it comes to trying to understand a large codebase, the benefit of knowing immediately that 2 structures are related by way of interface just seems essential. Code navigation becomes much more difficult and the benefit of not having coupled packages just isn’t great enough in my opinion for this to be justified."


#10

The idea of implicit interfaces in a programming language for application development such as Kotlin would be to have a bridge to cope with legacy issues you have to live with such as the Map/Collection problem mentioned in my previous post. I believe implicit interfaces also offer flexibility for other things. In that way implicit interfaces would accompany explicit ones and would be used as a lender of last resort.

Not being able to declare some class to implement some interface in Go also made me think. My understanding of system programming is limited, but I believe these people that made Go, who have been doing system programming on a high level for their entire career (operating systems, compilers, etc.), have some clue why they want to avoid unwanted dependencies at all cost. For system programming this seems to be vital. In the end, Go is a modernized C, which is sometimes overlooked.

– Oliver


#11

As I mentioned before, I'm leaning slightly against implicit interfaces, however, just to be thorough, I have to point out an argument in their favor.

Extension methods make it possible for Kotlin to add methods to existing classes without having to modify these classes. From this perspective, it may be reasonable to instruct the compiler that these extended classes now implement interfaces they didn’t used to before. For example, if I add a “run()” method to an existing class, it would be nice to be able to tell the compiler “By the way, this class now implements Runnable”.

This is beginning to look like Haskell’s type classes, a feature that has many good properties.

Note that this suggestion is still not exactly an implicit interface, it’s explicit but using the extension mechanism. Maybe the best of both worlds?


#12

My understanding of system programming is limited, but I believe these people that made Go, who have been doing system programming on a high level for their entire career (operating systems, compilers, etc.), have some clue why they want to avoid unwanted dependencies at all cost. For system programming this seems to be vital. In the end, Go is a modernized C, which is sometimes overlooked.

I recently had a quick look at Python and now I understand the Go people: they are basically trying to provide a language that is type safe and (reasonably) fast (at the moment Go can just compete with Java in speed), but has the feel and productivity of a scripting language - in particular like Python. If you look at Python you see many things they have taken over to Go exactly the same way. Not having exceptions was taken over from C, though ;-).

– Oliver


#13

I would like some language support to avoid having to create multiple methods for objects from external libraries that have the same methods but do not implement the same interface.  For instance, braintree had a number of classes that all have the method getCustomers() returning a List<Customer>.  If you could create an interface and cast these different classes to it that might solve the problem.  

It might even be enough to be able to create an interface inline as part of the method signature if it only has one method that you care about.  This might get ugly.

This is the use case I run into often.  And its nice to have language features that help you wrap these external libraries without having to use reflection.


#14

It makes sense not to do this by default due to JVM limitations. But what about making the conversion explicit on the call site? Something like:

fun main(args:Array<String>) {   bar(SomeTrait by SomeClass)   bar(by SomeClass) //when not ambiguous }

fun bar(a : SomeTrait) = println(a.foo())

trait SomeTrait {
  fun foo() : Int
}

class SomeClass {
  fun foo() : Int = 3
}


#15

This is doable, but I'm not sure whether it will make many people happy


#16

What if it was something more like:

fun main(args:Array<String>) {
    val someClass = SomeClass()
    bar(someClass as SomeTrait)
}

Basically Coercing it into the trait.


#17

Making the same operation ("as") do absolutely different things depending on the context is a rather risky thing


#18

The "Nice Programming Language" has the concept of Abstract Interfaces, http://nice.sourceforge.net/manual.html#abstractInterfaces.  It would be great to have some special operator to cast an object to an interface/trait which the object statisfies but did not extend.


#19

That is very much in line with what I would like to see.  I like the "you would be better off with a dynamic language" argument.  And in this case the interface isn't applied so much as coerced.


#20

With the "Nice Language" Abstract Interfaces are special and can only be used as parameterized type bounds.  Example Kotlin Syntax

abstract trait DisplayNamed {
  fun getDisplayName(): String
}

fun <T:DisplayNamed> sayHello(named: T) {
  println(“Hello ${named.getDisplayNamed()}”);
}

class Person(val first: String, val last: String)

// sayHello(Person(“Bill”,“Murray”))  – Error Person does not have method getDisplayName

fun Person.getDisplayName(): String = first+" "+last

sayHello(Person(“Bill”,“Murray”))  // works!