https://kotlinlang.org logo
#announcements
Title
# announcements
a

Andy Gibel

02/05/2021, 6:06 PM
Hi all - qq about what's possible with Kotlin: Assume I want to convert between two types. One of type
interface A
and one of
interface B
is there a way to write a generic convert function using 
inline reified
 that will convert A to B if and only if a conversion function 
fun(a) : B
  exists?
m

Marc Knaup

02/05/2021, 6:08 PM
Not without writing a compiler plugin. You cannot check for the existence of functions (without reflection, which is problematic).
The only option is implementing a conversion interface, e.g.
interface A: ConvertibleToB
but that’s not very generic.
ConvertibleTo<T>
would be an option, but also has various downsides
n

Nir

02/05/2021, 6:11 PM
One of the prices you pay for constrained generics is that these things become hard
a

Andy Gibel

02/05/2021, 6:12 PM
Ok this is kind of what I expected 🙂.
ConvertableTo<T>
seems like a reasonable approach... This would in turn just replace a build in version of "check if this function exists" right?
m

Marc Knaup

02/05/2021, 6:13 PM
Yeah that’s what interface are for 🙂 But the function will have to be part of
A
. Also, you can only convert to a single type. You cannot do
A: ConvertableTo<B>, ConvertableTo<C>
n

Nir

02/05/2021, 6:14 PM
I guess my question would be, what is actually the generic part of the code here?
You said you want to have a generic conversion function, but the conversion is actually type specific, and does A to B or whatever
so, you have some generic function, that does something beyond just conversion, but needs to do conversion as part of its work. And you want it to be able to do the conversion generically?
m

Marc Knaup

02/05/2021, 6:17 PM
Alternatively you could build a
KClass
-based conversion registry. And then
Copy code
fun aToB(a: A): B {…}
…
conversionRegistry.add(::aToB)
…
val b = conversionRegistry.convertOrNull<B>(a)
But that only checks types at runtime.
n

Nir

02/05/2021, 6:18 PM
In simpler use cases you could just ask the user to pass a lambda that does the conversion; that is very simple and flexible and honestly how I work around many of these issues with kotlin's type system
Copy code
fun <A, B> do_work(someParam: Param, converter: (A) -> B) { ... }
m

Marc Knaup

02/05/2021, 6:19 PM
We’re basically guessing here without further info/context 😄
n

Nir

02/05/2021, 6:19 PM
instead of
do_work
"just knowing" how to convert an A to B based on some assumed trait.
Yeah
🙂
a

Andy Gibel

02/05/2021, 6:22 PM
Hm yeah so not to drag you too deep into the weeds but the tl;dr; is this: I'm modeling my app as independent reactive "widgets" each with a
postInput(input : InputType)
output() : Observable<OutputType>
I'd like to be able to tie these widgets together in the fashion:
Copy code
widget1.output().subscribe{    widget2.postInput(it.toWidget2Input) 
}
However I'd like to have this abstracted so that caller code could connect widgets with something like a DSL
Copy code
val widgetConnections = connect {
  widget1 to widget2,
  widget1 to widget3,
  widget2 to widget4,
}
but this would require that I have some generic way to convert between inputs and outputs
m

Marc Knaup

02/05/2021, 6:24 PM
What’s a realistic maximum number of output types an input can convert to? And who would define the conversion? The input type? The output type? Or a third party?
There would be a way but only with a new Kotlin feature that is being worked on: multiple receivers.
a

Andy Gibel

02/05/2021, 6:25 PM
I'd say WidgetOutputs have an upper bound of about 4. Right now I have the conversions defined as top level extension functions on the outputs but all that is up in the air 🙂
n

Nir

02/05/2021, 6:25 PM
if the types don't match exactly, I'd just add the converter to your DSL
Copy code
val widgetConnections = connect {
  widget1 to { foo(it) } to widget2,
  widget1 to widget3,
  widget2 to widget4,
}
a

Andy Gibel

02/05/2021, 6:26 PM
oh fair point. I could define converters as
Copy code
class ConverterToCX() { invoke()...}
n

Nir

02/05/2021, 6:27 PM
you'd need to "hijack"
to
to make this work so you may want to use a different infix function at that point
Well, converters would just be any callable
this is just very close to function composition rules at this point
m

Marc Knaup

02/05/2021, 6:28 PM
Copy code
infix fun [ConnectScope, Widget1].to(output: Widget2) {
   // register your conversion function
}
Basically each conversion function is an extension function for your DSL scope. But doesn’t work with today’s Kotlin. This approach needs multiple receivers.
👍 1
n

Nir

02/05/2021, 6:28 PM
you can connect a widget that outputs an X to a function converting X to Y, to "effectively" get a widget that returns a Y. And a widget that returns a Y can be connected to a widget that accepts a Y
you could do Marc's approach in modern Kotlin (as I understand it) by simply using a global
a

Andy Gibel

02/05/2021, 6:30 PM
Cool I think I will just simplify with explicit converters passed via DSL. Thanks guys!
👍 1
n

Nir

02/05/2021, 6:30 PM
Copy code
object Connector {
    fun register()  // okay register converters here
}

fun connect(user_dsl: Connector.() -> Unit) { ... }
m

Marc Knaup

02/05/2021, 6:31 PM
If you can define the conversion function in either
Input
or
Output
widget type then you can use the
ConvertibleTo<…>
approach, but with different variants.
Copy code
A: ConvertibleTo1<B>
A: ConvertibleTo2<B,C>
A: ConvertibleTo3<B,C,D>
// etc.
or
Copy code
A: ConvertibleTo1<B>, ConvertibleTo2<C>, ConvertibleTo3<D>
// etc.
And then add DSL for
ConvertibleTo1<T>
,
ConvertibleTo2<T,U>
,
ConvertibleTo3<T,U,V>
etc.
a

Andy Gibel

02/05/2021, 6:33 PM
NIce I will definitely play with this as well and will be keeping an eye out for those multiple receivers. Thanks again!
2 Views