Hacker News new | ask | show | jobs
by danneu 3148 days ago
Good job reaching a stable release.

I have a Kotlin microframework that never made it past hobby-level stability[1].

One thing I found out is that you really have to write a library in Java if you want it to be used in both Java and Kotlin. Java -> Kotlin is effectively one-way interop.

I also found async programming to be really hard in Java which is why I wrapped Jetty. Meanwhile async APIs like Netty and Undertow were completely exotic to me.

For example, I couldn't figure out how to go from `Request -> Response` to `Request -> Promise<Response>` by wrapping Netty.

One thing I did find out was that Kotlin is probably my favorite language. Very similar to Swift, though I wish you could re-open 3rd party classes to make them conform to additional interfaces which you can do with Swift protocols.

I also never figured out how to hot reload code without restarting the server. Even JRebel didn't work for me. Looking at Jetbrains' own framework, their code that implements reloading is pretty intimidating[2].

OP is also the author of https://github.com/tipsy/j2html which I've been using in my own small servers. I couldn't figure out a better way to get typesafe html templating in the ecosystem.

[1]: https://github.com/danneu/kog [2]: http://ktor.io

6 comments

This is definitely NOT TRUE. I've written a library which is used from both Java and Scala and it just works. Here: https://github.com/Hexworks/zircon

Just because you didn't manage to get it working does not mean that interop is not two-way.

> Java -> Kotlin is effectively one-way interop

Could you provide some more details? I'm a bit surprised by your statement because Kotlin team has put a lot of effort into providing mostly seamless two way interop. Kotlin project itself is ~50% Java (specifically to test interop).

I'm really rusty, but if I can recall the simplest example correctly, you can't just naively go from this Kotlin code:

    Server { req -> Response().html("<h1>Hello world</h1>") }
to this Java code:

    new Server(req -> new Response().html("<h1>Hello world</h1>") 
Because Server is implemented in Kotlin and the Java lambda certainly isn't a kotlin.Function.

If that's a bad example (foggy memory), another one would be the use of @DslMarker or reflection to build my type-safe router: https://github.com/danneu/kog#type-safe-routing. Or how about inline functions with reified generics.

Kotlin does have Kotlin->Jvm interop annotations, but my take-away was that you would have to put some thought into your API and possibly make concessions so that you could sufficiently annotate it to the point that it's pleasant to use from Java. Not everything maps.

For example, you can see that OP went the route of using Java at the interface edges yet Kotlin internally to solve interop for their project.

I admit that "effectively one-way interop" is too heavy-handed of a statement, but there's definitely asymmetry that I ran into on my own project. For example, I would not recommend building something in Kotlin in the hopes that, once you're done, you can just sprinkle on some @Jvm annotations and end up with an API that's compelling to use from Java.

That's because the concept of a typed lambda function doesn't really exist in Java; in Java 8 lambdas are syntactic sugar for SAM-type object creation. (There are other interface warts too: @DslMarker and parameterized receivers, as you mention; treatment of void vs. Unit; different default collection types; destructuring data types, and the need to clutter your code with 'new' if you use them a lot; etc.)

It's similar to any interop problem: if you want your library to be accessible from lots of languages, its interface better be the lowest common denominator of the features they support.

There's nothing stopping you, though, from defining your interfaces in Java and then implementing those interfaces in Kotlin. The Kotlin interop code even will do a good job letting you use Kotlin's syntactic sugar for this, eg. you can pass Kotlin lambdas to functions that expect a SAM-type, or use 'with()' to treat a conventional builder pattern as a DSL.

Hasn't really been my experience - I've found it pretty easy to write eg. Hadoop classes, Rome plugins, servlets from Kotlin. You do need to be aware of what code Kotlin is generating for you, and there are occasional warts, but in general you just implement your interfaces and enjoy the boilerplate that Kotlin frees you from typing.
I don't have interop issues with your examples either. Calling Java from Kotlin is very pleasant.

What I found is that it's the other direction that takes some thought. For example, my Kotlin library needs more help than just @Jvm annotations to be usable from Java.

All my examples are calls into Kotlin from Java. With Hadoop, you're implementing interfaces that the framework instantiates via reflection and then the Java framework calls the Kotlin code. With Rome, you're creating Kotlin objects that implement a Java interface and then they're called from Java code. With servlets, the servlet container (Jetty, in this case) holds a reference to the servlet and then calls its serve() method upon an HTTP request.
You are surprised because it is not true. I can prove that you can write two-way interop just check the library I've written and posted above.
You can write two-way interop -- I didn't mean to suggest otherwise.

But there are Kotlin features that don't map like type-safe @Dsl's which puts you in a position where you have to make concessions for interop.

Now that is true. You also can't use reified generics (which is a bit hacky anyway).
"It totally works...except for A, B....oh yeah and C, but C kinda sucks....well D is awful too, you should never use D...."

So basically, he was right, and you weren't. There is no seamless 2-way interop, yes?

I don't understand this sentiment. I understand Kotlin has more features than Java, and of course it's tough to call the advanced things from Java. At least so I would think.
No he was not right. You can write two-way interop and it is not hard. I have actually written a library which does it. He did not say that "the two-way interop is not seamless". He said that you "can't write two way interop code" which is not true.
> One thing I found out is that you really have to write a library in Java if you want it to be used in both Java and Kotlin. Java -> Kotlin is effectively one-way interop.

Hah. As someone who remembers the early days of Scala - the promise of seamless interop followed by the realisation that a language-idiomatic API requires much more than being able to seamlessly invoke a method - I can't say I'm surprised.

> One thing I did find out was that Kotlin is probably my favorite language. Very similar to Swift, though I wish you could re-open 3rd party classes to make them conform to additional interfaces which you can do with Swift protocols.

Maybe look at Scala; implicit conversions offer an easy-but-somewhat-hacky way to do that, while typeclasses are a very elegant general solution that a lot of libraries are based on these days.

"One thing I found out is that you really have to write a library in Java if you want it to be used in both Java and Kotlin. Java -> Kotlin is effectively one-way interop."

Could you please explain this.

I think it means that a library written in Kotlin is difficult or impossible to use from Java.
Not really true from my experience. For example, here's a Markdown library written in Kotlin that you can use with a plain Java interface: https://github.com/valich/intellij-markdown
No no no no. Kotlin provides a lot of tools to make your library usable from Java and it works.
You have to write your Kotlin anticipating it'll be called from Java though (e.g. by using SAM interfaces instead of function types for your arguments)
This does not mean that it is a one-way interop. When you write it the right way, it will work from Java.
Is Kotlin's function not an SAM interface? I'm pretty sure Scala's is.
> though I wish you could re-open 3rd party classes to make them conform to additional interfaces which you can do with Swift protocols.

Mmmh really? I just reread the Protocol section of the Swift documentation and I didn't find this.

If I have a class I can't modify and that doesn't conform to protocol `Foo`, can I make it conform to that protocol?

A similar feature is being considered for Kotlin since this opens up all kinds of interesting mechanisms (notably, ad hoc polymorphism) but I don't see how to do that in Swift right now.

> If I have a class I can't modify and that doesn't conform to protocol `Foo`, can I make it conform to that protocol?

Yes, you can.

Not sure if that's a good thing though. I read some of the Swift standard library source code and found it very difficult to piece together all the stuff added by extensions, where they come from and where the original class definition is.

Obviously, it's going to be easier if you have written the code yourself, but what if someone else needs to read it?

Can you point to the relevant documentation section? I couldn't find it.
Thanks! Yeah, I had to leave a few parts as Java in order to get the interop right. Anything that takes an functional interface needs to be Java, so the main API entry points are all Java. Most other classes and all internal code is Kotlin. I originally wrote it all in Java, and ended up with about 30% less code after I rewrote it to Kotlin.