Open trustin opened 6 years ago
@tobias- IIRC your Armeria-based project is written in Kotlin. It'd be awesome if you can tell us any issues you've encountered so far that makes your code unnecessarily verbose.
Most(all) of my kludges/enforced ugly code are around lambda functions, especially with decorators. The syntax for them is slightly different in Kotlin, and the compiler seems less able to figure out what generics are needed.
I'll let you know if I can think of anything else.
-- Edit: Found HttpService and SimpleDecoratingHttpService and removed all my examples
I still wish to know the before and after of your code. Would you mind pasting it?
Sure. The first example is still not working without a lot of generics: https://gist.github.com/tobias-/cbdc441872d2256b1d7a318b54ae057e
Scala example:
object Test {
import brave.Tracing
import com.linecorp.armeria.client.ClientOptionsBuilder
import com.linecorp.armeria.client.brave.BraveClient
import com.linecorp.armeria.client.logging.LoggingClient
// ok
val a = new ClientOptionsBuilder()
.decorator(LoggingClient.newDecorator())
.build()
// can't compile:
// [error] /path/to/Test.scala:62:6: overloaded method value decorator with alternatives:
// [error] [I <: com.linecorp.armeria.common.HttpRequest, O <: com.linecorp.armeria.common.HttpResponse](decorator: com.linecorp.armeria.client.DecoratingClientFunction[I,O])com.linecorp.armeria.client.ClientOptionsBuilder <and>
// [error] [T <: com.linecorp.armeria.client.Client[I,O], R <: com.linecorp.armeria.client.Client[I,O], I <: com.linecorp.armeria.common.HttpRequest, O <: com.linecorp.armeria.common.HttpResponse](decorator: java.util.function.Function[T,R])com.linecorp.armeria.client.ClientOptionsBuilder
// [error] cannot be applied to (java.util.function.Function[com.linecorp.armeria.client.Client[com.linecorp.armeria.common.HttpRequest,com.linecorp.armeria.common.HttpResponse],com.linecorp.armeria.client.brave.BraveClient])
// [error] .decorator(BraveClient.newDecorator(Tracing.current()))
// [error] ^
// [error] one error found
val b = new ClientOptionsBuilder()
.decorator(BraveClient.newDecorator(Tracing.current()))
.build()
// ok
val c = new ClientOptionsBuilder()
.decorator[Client[HttpRequest, HttpResponse], BraveClient, HttpRequest, HttpResponse](
BraveClient.newDecorator(Tracing.current())
)
.build()
}
@rfkm Looks similar to my problem? Basically you need to force-cast with disabled warnings to get the right generics?
One thing I'd like is not to have to use Optional<> in annotated rest interfaces. If a variable is marked as @Nullable
(or one of the other 3-4 annotations with the same purpose) it would allow missing fields annotated with e.g. @Header
. In Kotlin, that would mean a String?
instead of Optional<String>
and it would work better with kotlin-specific operators designed to handle nullability. If IntelliJ is used to program Java, annotating with @Nullable
would probably be as good as Optional for clarity in code.
I'd like to assign this issue to @ikhoon because he knows Scala knows well. :wink: It'd be nice to have Scala and Kotlin (and Clojure?) examples that demonstrate smooth integration. I hope to have this as a part of our 1.0.0 release.
I need to get some hours to check what happens with the changes in 0.96 to decorating and HttpService. It might be that the need for ugly-casting is gone.
I'd like to assign this issue to @ikhoon because he knows Scala knows well. 😉 It'd be nice to have Scala and Kotlin (and Clojure?) examples that demonstrate smooth integration. I hope to have this as a part of our 1.0.0 release.
I missed your mention. 😠I like to handle this issue before 1.0.0 release 🥰
Ok, 0.96.0 upgrade complete. Extending the api with HttpService accepting methods did help, but still a lot of weird casts/specification of interfaces. First instinct is to push this for the future and implement a kotlin extension lib with methods for decorating, getting coroutine support etc etc.
Some loose thoughts as a Scala programmer:
It would be great if it was easy to integrate with fs2-based code in cases where we want to be truly async all the way through (i.e. streaming the http response).
Armeria supports Reactive Streams. that means you can easily interop any stream library that supports Reactive Streams. https://fs2.io/guide.html#reactive-streams
Scrooge offers a more idiomatic Scala implementation of thrift. Their server-side support was hardcoded to use Twitter futures at one point, but it's fairly easy to add a template for a different kind of future (I used the one in my fork m50d/scrooge at a previous job)
Armeria runs on the top of HTTP protocol such as H1, H1C, H2, and H2C. IIUC, scrooge supports TCP :-( Alternatively, how about using gRPC with ScalaPB?
It would be nice if we could use armeria as a backend for routes defined in rho
That sounds good. Are you interested in making a PR? 😀
Scala example:
object Test { import brave.Tracing import com.linecorp.armeria.client.ClientOptionsBuilder import com.linecorp.armeria.client.brave.BraveClient import com.linecorp.armeria.client.logging.LoggingClient // ok val a = new ClientOptionsBuilder() .decorator(LoggingClient.newDecorator()) .build() // can't compile: // [error] /path/to/Test.scala:62:6: overloaded method value decorator with alternatives: // [error] [I <: com.linecorp.armeria.common.HttpRequest, O <: com.linecorp.armeria.common.HttpResponse](decorator: com.linecorp.armeria.client.DecoratingClientFunction[I,O])com.linecorp.armeria.client.ClientOptionsBuilder <and> // [error] [T <: com.linecorp.armeria.client.Client[I,O], R <: com.linecorp.armeria.client.Client[I,O], I <: com.linecorp.armeria.common.HttpRequest, O <: com.linecorp.armeria.common.HttpResponse](decorator: java.util.function.Function[T,R])com.linecorp.armeria.client.ClientOptionsBuilder // [error] cannot be applied to (java.util.function.Function[com.linecorp.armeria.client.Client[com.linecorp.armeria.common.HttpRequest,com.linecorp.armeria.common.HttpResponse],com.linecorp.armeria.client.brave.BraveClient]) // [error] .decorator(BraveClient.newDecorator(Tracing.current())) // [error] ^ // [error] one error found val b = new ClientOptionsBuilder() .decorator(BraveClient.newDecorator(Tracing.current())) .build() // ok val c = new ClientOptionsBuilder() .decorator[Client[HttpRequest, HttpResponse], BraveClient, HttpRequest, HttpResponse]( BraveClient.newDecorator(Tracing.current()) ) .build() }
@rfkm This is fixed by @minwoox 's work #2239 😉
What do you think about adding out-of-the-box Option[]
type support to annotated services? Could probably load and instantiate the scala.Option
class dynamically? I can take a stab if it makes sense and is doable with small amount of modification. Thoughts, @rfkm and @ikhoon ?
@tobias- wrote:
One thing I'd like is not to have to use Optional<> in annotated rest interfaces. If a variable is marked as
@Nullable
(or one of the other 3-4 annotations with the same purpose) it would allow missing fields annotated with e.g.@Header
. In Kotlin, that would mean aString?
instead ofOptional<String>
and it would work better with kotlin-specific operators designed to handle nullability. If IntelliJ is used to program Java, annotating with@Nullable
would probably be as good as Optional for clarity in code.
This has been resolved via #2766. Let me know what you think. 🙇
What do you think about adding out-of-the-box
Option[]
type support to annotated services? Could probably load and instantiate thescala.Option
class dynamically? I can take a stab if it makes sense and is doable with small amount of modification. Thoughts, @rfkm and @ikhoon ?
If scala.Option
could be supported with small amount of changes, it makes sense.
FYI
Null
type. The Scala 3 compiler has better Java interop for nullable type.
https://dotty.epfl.ch/docs/reference/other-new-features/explicit-nulls.html#java-interopI know, use and like it! :+1:
Thanks :bow:
@tobias- wrote:
One thing I'd like is not to have to use Optional<> in annotated rest interfaces. If a variable is marked as
@Nullable
(or one of the other 3-4 annotations with the same purpose) it would allow missing fields annotated with e.g.@Header
. In Kotlin, that would mean aString?
instead ofOptional<String>
and it would work better with kotlin-specific operators designed to handle nullability. If IntelliJ is used to program Java, annotating with@Nullable
would probably be as good as Optional for clarity in code.This has been resolved via #2766. Let me know what you think.
IMO, Most Scala users prefer routing DSL than annotated service, unlike Java users. In the future, I think, it would be better to add a DSL layer for adopting Scala users.
Yes, I agree. However, instead of creating a new DSL, it may be better to provide Armeria implementation to existing libraries (e.g. sttp, tapir, http4s, etc). For example, sttp is a popular HTTP client facade in Scala, but the one of most commonly used backend implementations is AsyncHTTPClient which doesn't support HTTP2. Contributing to their repositories might help more Scala users know about Armeria 😄
For example, sttp is a popular HTTP client facade in Scala,
I was personally working on sttp for Armeria backend. But I stopped because of multipart issue #253
I’ll resume it once #2894 is merged and released. 😀
@rfkm @m50d You might be interested in http4s/http4s#3614 PR. 😉
Let me unassign a milestone from this issue, because it's actually on-going effort spans across many releases.
Most notably Scala, Kotlin and Clojure. We could write a sample project that makes sure at least: