Hi all - qq about what's possible with Kotlin: As...
# announcements
a
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
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
One of the prices you pay for constrained generics is that these things become hard
a
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
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
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
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
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
We’re basically guessing here without further info/context 😄
n
instead of
do_work
"just knowing" how to convert an A to B based on some assumed trait.
Yeah
🙂
a
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
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
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
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
oh fair point. I could define converters as
Copy code
class ConverterToCX() { invoke()...}
n
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
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
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
Cool I think I will just simplify with explicit converters passed via DSL. Thanks guys!
👍 1
n
Copy code
object Connector {
    fun register()  // okay register converters here
}

fun connect(user_dsl: Connector.() -> Unit) { ... }
m
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
NIce I will definitely play with this as well and will be keeping an eye out for those multiple receivers. Thanks again!