https://kotlinlang.org logo
#compiler
Title
# compiler
e

eygraber

02/22/2024, 4:38 AM
I was expecting to get an unchecked cast warning here where I do
k as C
, especially since I got an error when I tried
lookup[C::class] = factory
despite
C
being guaranteed to be a
K
. Anyone know why I didn't get a warning?
Copy code
public class FooFactory<K : FooKey> {
  @PublishedApi internal val lookup: MutableMap<KClass<out K>, (K) -> Foo<out K>> = mutableMapOf()

  public inline fun <reified C : K> addFactory(
    noinline factory: (C) -> Foo<out C>
  ) {
    lookup[C::class] = { k ->
      factory(k as C)
    }
  }
}
d

dmitriy.novozhilov

02/22/2024, 5:48 AM
C
is
reified
parameter, so it will be inlined to specific type argument on each call site, which makes this cast not unchecked
e

eygraber

02/22/2024, 5:51 AM
Even though the
k
parameter to the lambda is typed as
K
? So if
C
was
FooKey.Bar
wouldn't the cast be
k: K as FooKey.Bar
?
d

dmitriy.novozhilov

02/22/2024, 5:53 AM
Yes, that's right And because of type erasure, at runtime it will be
k: FooKey as FooKey.Bar
e

eygraber

02/22/2024, 5:54 AM
But then I could do
lookup[FooKey.Bar::class]?.invoke(FooKey.Baz)
which I would think would result in a ClassCastException
d

dmitriy.novozhilov

02/22/2024, 5:58 AM
Not sure I understand what you mean Could you provide the full snippet?
e

eygraber

02/22/2024, 6:13 AM
That's all the code I currently have, but theoretically I could have
Copy code
sealed interface FooKey {
  data object Bar : FooKey
  data object Baz : FooKey
}
and because I wrap the factory with a cast that assumes
K
is
C
, I could later retrieve a factory for
C
, but pass it something that isn't
C
as the
K
parameter which would cause a ClassCastException
d

dmitriy.novozhilov

02/22/2024, 6:16 AM
Well, actually any
as
cast may cause
ClassCastException
And in your original code there are type constraints, which are checked at compile time
Copy code
C <: K <: FooKey
So you can not pass such
C
which won't be subtype of
K
e

eygraber

02/22/2024, 6:18 AM
So then why is it an error to say
lookup[C::class] = factory
?
d

dmitriy.novozhilov

02/22/2024, 6:24 AM
Copy code
lookup (value): (K) -> Foo<out K> = Function1<K, Foo<out K>>
       factory: (C) -> Foo<out K> = Function1<C, Foo<out K>>
Function1
declared as
Function1<in T, out R>
factory
may be subtype of
lookup
if
in
argument of
factory
is supertype of the corresponding argument of
lookup
, which is not correct (
C <: K
, not vice versa)
e

eygraber

02/22/2024, 6:26 AM
Ah didn't realize that the parameter is
in
but I guess that makes sense 😅
d

dmitriy.novozhilov

02/22/2024, 6:28 AM
Actually, Kotlin type system just can't express the relation between key and value, which you put in the
lookup
map You has a relation that type of value depend on the type of specific key, which is possible only in system with dependent types (like in Agda or Arend)
Copy code
lookup: MutableMap<K' : K, KClass<out K'>, (K' -> Foo<out K')>
Sorry, don't remember exact notation which makes such definitions clear
e

eygraber

02/22/2024, 6:32 AM
Thanks, it's pretty clear now. I was hoping that using the class type parameter would help with this, but I guess it just doesn't work like that
👍 1
3 Views