I'm trying to cram out some static dispatch from a...
# announcements
h
I'm trying to cram out some static dispatch from a reified type, but no luck so far:
Copy code
fun foo(dummy: Int?) {}
fun foo(dummy: String?) {}

// This causes a compilation error.
// You get 
//   reified.kt: (8, 5): None of the following functions can be called with the arguments supplied: 
//   public fun foo(dummy: Int?): Unit defined in root package in file reified.kt
//   public fun foo(dummy: String?): Unit defined in root package in file reified.kt
inline fun <reified T> bar() {
    // As the function is inlined and the type parameter reified, I'm hoping for static dispatch,
    // i.e. depending on T, one of the overloads for foo() should be chosen.
    foo(null as? T)
}

// This function has no problems compiling, static dispatch works as expected.
fun baz() {
    foo(null as Int?)
    foo(null as String?)
}
Why do I get a compilation error from
bar
calling
foo
? I guess it has something to do with Kotlin instantiating the
bar
function even though it's supposed to be inlined yet never called (with an actual type parameter), but I haven't found the reason yet (I read https://kotlinlang.org/spec/runtime-type-information.html#runtime-available-types but found no explanation).
n
Are you coming from C++ by chance? ๐Ÿ™‚
h
lol
n
even if the type parameters are reified, kotlin generics are constrained
h
oh yes i am ๐Ÿ™‚
n
unlike C++ templates
You can only do with a
T
whatever the constraint says you can do
in this case there's no constraint
h
ok, but what about the type checking of the inlined function? how can that be made before knowing what
T
will be
n
Not sure i follow
h
ok. so Kotlin says that when not knowing
T
,
T
has no constraints?
n
The whole point of constrained generics is that a generic function/class can be type checked in isolation
in C++, this isn't the case, the template isn't really type checked fully until somebody uses it, and its checked for their specific case
h
ah, exactly
n
T has no constraints
So for instance, imagine if somebody did
bar<Float>
that wouldn't work
h
ok, i get it. thanks
n
even though
Float
meets the constraints of bar (trivially, because there are no constraints)
the nice thing about constrained generics (which is what you see in almost every non C++ language) is that an error is always distinctly either the the generic function's, or the users
either the generic function does something that's not part of the constraint, or the user passes a type that doesn't meet the constraint. It's like a contract.
In C++ this is all very hazy.
But on the flip side for this and many other reasons, Kotlin's type system is a lot less powerful. You really dont' want to try to push Kotlin's type system to the limits, like C++... you hit those limits very quickly and it's not satisfying ๐Ÿ™‚
h
btw, the inlined function is type checked when parsed, but is it instantiated when used like in C++, or does Kotlin do something funky behind the scenes, e.g. half-compiling the inlined function before it is instantiated (or "used" is maybe more proper Kotlin lingo)?
n
I'm not sure about the implementation details, to be honest.
but kotlin inline functions are pretty literally inlined
h
yeah, i feel great dissatisfaction right now ๐Ÿ™‚ and so i've done so far almost every time i tried to push the boundaries
n
so the code probably gets dumped in there pretty early
one thing that's awesome about kotlin inline functions (that I miss in C++ ๐Ÿ™‚ ) is that because they are really inline, when you pass lambdas into inline functions, those lambdas can return on the outer function
h
"pretty literally", "probably"... i don't mean to be rude, but that means i'll probably have to look it up in the code myself ๐Ÿ™‚
n
Sure. If you want to know for educational reasons, by all means. If you're thinking about performance though, I wouldn't think of performance that way in Kotlin.
Even worrying about static dispatch in Java or Kotlin is kind of eh
h
nah, i want to know just so that i know the boundaries of the language
n
Everything is pretty dynamic compared to C++, but because it's JIT'ed predictable but theoretically dynamic calls can be optimized a lot better than in C++
h
i want to use the knowledge for ergonomic tricks, not for performance
for performance i'd go native with rust, c++ or assembly
n
For ergonomics, you only really need to worry about the language rules though ๐Ÿ™‚ . When/how inline functions get codegened don't really matter.
h
you're probably right
n
yeah, that makes sense.
the single worst thing probably in kotlin is that your generics are constrained, but the constraints work purely on inheritance
that means that when you want to be generic over say a few types you don't control, it's a mess
since you can't add base classes/interfaces to classes that are not yours
h
i don't think i follow you. "the constraints work purely on inheritance"?
n
so constraints look like this
Copy code
fun <T : Comparable<T>> sort(list: List<T>) {  ... }
h
since you can't add base classes/interfaces to classes that are not yours
i think there's an issue for adding external interfaces though...
n
there's a KEEP for it, yes
h
yup, that's a constraint.
n
but it's not on the 6 month roadmap
yeah, so this means that this will only work for classes that inherit from Comparable<T>
h
yep
n
if there is some class that you want to define a comparator for, and use here, you're SOL. need to define a wrapper.
A good way to see the limitations of Kotlin's type system quickly is to try writing an in-memory representation of json
h
ah, now i see what you meant by "purely on inheritance". you meant that we don't have those external interfaces yet, basically
n
in some languages you can start with something as simple as
Json = Nothing | String | Float | List<Json> | Map<Json>
In C++ because it has value semantics, you need to be careful with recursive types and may need extra indirection, but basically you can do that with a
variant
in kotlin, it becomes a mess really quickly
there's no real sum type, you have to use a sealed class. but then there's the rub: you can't make
String
,
Float
, etc inherit from your sealed class
so you need a wrapper, but then the wrapper cannot be generic because of type erasure ๐Ÿ™‚
etc
h
i think i brushed against similar issues in my current work. it feels familiar.
speaking of which, i should get back to my real work. thanks for clarifying!
n
np!