Olaf Gottschalk
05/31/2024, 7:31 AMimport arrow.core.EitherNel
import arrow.core.right
interface ConfigScope
interface ConfigError
typealias Validated<A> = EitherNel<ConfigError, A>
object ConfigurationContext
object CreationContext
typealias Configuration<C> = context(ConfigurationContext) ConfigScope.() -> Validated<C>
typealias Creation<C, T> = context(CreationContext) C.(C) -> Validated<T>
class Definition<C, T>(
val name: String,
val configuration: Configuration<C>,
val creation: Creation<C, T>,
)
class Source
fun sources(block: BuilderDsl<Source>.() -> Unit) = BuilderDsl<Source>().apply(block).things
class BuilderDsl<T> {
val things = mutableMapOf<String, Definition<*, T>>()
infix fun <C> String.configuredBy(config: Configuration<C>) = Pair(this, config)
infix fun <C> Pair<String, Configuration<C>>.createdBy(creator: Creation<C, T>) {
Definition(first, second, creator).also {
things[it.name] = it
}
}
}
fun main() {
sources {
"Dummy" configuredBy { 42.right() } createdBy { Source().right() }
}
}
It's a little mini DSL to define how to create something with a two step approach: first configuration, then later creation. The DSL sources
is there to allow the definition of a specific source.
The problem with Kotlin 2.0 lies in the context receivers on the typealiases Configuration
and Creation
- which are needed in real life to supply some more, you guessed it, context. K2 is not able to correctly create the two lambdas in the main
function. You get this error message on the lambda after configuredBy
and `createdBy`:
Argument type mismatch: actual type is 'kotlin.Function2<ConfigScope, ConfigScope, Validated<C>>', but 'Configuration<C>' was expected.
I do not understand why this is. Why does K2 not just create the required lambda here and what could I do to force it to "take" the correct type?
Thanks!!Olaf Gottschalk
05/31/2024, 7:50 AMtypealias
that creates this problem! If I remove the Configuration<C>
and Creation<C, T>
type aliases and instead declare the exact same thing in the fun configuredBy
and createdBy
- it compiles!
infix fun <C> String.configuredBy(config: context(ConfigurationContext) ConfigScope.() -> Validated<C>) =
Pair(this, config)
infix fun <C> Pair<String, Configuration<C>>.createdBy(creator: context(CreationContext) C.(C) -> Validated<T>) {
Definition(first, second, creator).also {
things[it.name] = it
}
works... So, why is the typealias
in the way here? Is this a bug or a wanted change in behaviour?
Thanks!Olaf Gottschalk
05/31/2024, 7:52 AMAlejandro Serrano.Mena
05/31/2024, 7:52 AMOlaf Gottschalk
05/31/2024, 7:53 AMOlaf Gottschalk
05/31/2024, 7:54 AMAlejandro Serrano.Mena
05/31/2024, 8:01 AMcontext(A) (B) -> C
is identical, as a type, to (A, B) -> C
(or KFunction2
), and somehow the compiler has gotten confused about thismglukhikh
05/31/2024, 8:06 AMOlaf Gottschalk
05/31/2024, 8:06 AMOlaf Gottschalk
05/31/2024, 8:21 AM