Unfortunately I don't think there is a way to abstract over suspend and non suspend methods: once you go `suspend` then everything needs to `suspend`. I worked on a codebase some time ago that dealt with this and we just decided to always make interfaces suspend since once an implementation needs it then you're screwed. I guess this is the famous "function coloring" problem.
I think you’re right, and I’ve heard of other codebases with the same policy. Which is a real shame, because suspend functions are definitely harder to test and I assume less efficient to call. I wonder if, if they had their time again, the language designers would have made suspend a context, with special compiler handling, but at least passing the hidden CoroutineContext that way. Is there not a refactor to propagate suspend up the call hierarchy when you find you need it?
@@PairingWithDuncan I don't think passing the context would be enough since suspend functions are either compiled to a state machine or to continuations (I work mostly with C# where state machines are used, not sure about Kotlin). In C#, once you find something `async` you either force it to be sync (losing the benefits of async in the first place), our you need to propagate the async all the way to `main`. In my experience the best thing is to just start with `async`/`suspend` from main and move on. In languages like Scala or Haskell you can abstract this away since you have access to "generics of generics" (aka higher kinded types), allowing you to write interfaces where the methods/functions are parametrized over some "context" (ex. `fun foo(s: String) -> m Int`, where `m` can be a sync/async context)
Kotlin compiles suspend to a state machine too, that’s the special compiler handling that would have to come with the context. As I understand it, that means that every invocation of a suspend function from a suspend function requires the extra code, which means that we pay a performance and code size price. Kotlin’s context receivers, (soon, irritatingly, to be context parameters) will allow us to pass the async context kind of implicitly, but given the state machine issue, that doesn’t really help to generalise the calling, as you say.
I agree Kotlin has no way to abstract over function 'coloring'. In my mind, this is a good argument for http4k over ktor. Ktor is more complicated than http4k but in most cases there's no practical benefit.
Unfortunately I don't think there is a way to abstract over suspend and non suspend methods: once you go `suspend` then everything needs to `suspend`. I worked on a codebase some time ago that dealt with this and we just decided to always make interfaces suspend since once an implementation needs it then you're screwed. I guess this is the famous "function coloring" problem.
I think you’re right, and I’ve heard of other codebases with the same policy. Which is a real shame, because suspend functions are definitely harder to test and I assume less efficient to call.
I wonder if, if they had their time again, the language designers would have made suspend a context, with special compiler handling, but at least passing the hidden CoroutineContext that way.
Is there not a refactor to propagate suspend up the call hierarchy when you find you need it?
@@PairingWithDuncan I don't think passing the context would be enough since suspend functions are either compiled to a state machine or to continuations (I work mostly with C# where state machines are used, not sure about Kotlin).
In C#, once you find something `async` you either force it to be sync (losing the benefits of async in the first place), our you need to propagate the async all the way to `main`. In my experience the best thing is to just start with `async`/`suspend` from main and move on.
In languages like Scala or Haskell you can abstract this away since you have access to "generics of generics" (aka higher kinded types), allowing you to write interfaces where the methods/functions are parametrized over some "context" (ex. `fun foo(s: String) -> m Int`, where `m` can be a sync/async context)
Kotlin compiles suspend to a state machine too, that’s the special compiler handling that would have to come with the context. As I understand it, that means that every invocation of a suspend function from a suspend function requires the extra code, which means that we pay a performance and code size price.
Kotlin’s context receivers, (soon, irritatingly, to be context parameters) will allow us to pass the async context kind of implicitly, but given the state machine issue, that doesn’t really help to generalise the calling, as you say.
I agree Kotlin has no way to abstract over function 'coloring'. In my mind, this is a good argument for http4k over ktor. Ktor is more complicated than http4k but in most cases there's no practical benefit.