Question: I know this would not be possible with "...
# announcements
n
Question: I know this would not be possible with "regular" Java generics, but I thought maybe it is somehow possible with Kotlin reified generics. Basically, would like to be able to do something like:
Copy code
inline fun <reified T> foo() : Int {
    return T.bar
}
Basically, a way to access values, functions, etc, from the type itself, instead of from an instance of that type.
k
No duck typing please!
n
I'm not saying duck typing necessarily, I'm more than happy to constrain this with an interface
I just don't know how to do it so that this actually works
I tried all kinds of things with interfaces and companion objects and wasn't able to get anything to compile
d
I don't know how you would access an instance's property without a reference to an instance?
If you're looking to associate properties with the class itself (like
static
in Java) make sure you read about companion objects, if you haven't already?
r
@Nir You can access anything from the Type, but the Type is not the same as the companion.
n
I did read about companion objects, and tried various things, but couldn't make it work
Would explaining the use case be helpful?
d
It would definitely help 🙂
k
There's no "on the type" in Kotlin, that would be "on the companion object". Those can implement interfaces etc just fine, a quick sketch:
Copy code
interface X { val x: Int }
class Foo {
    companion object : X {
        override val x = 5
    }
}
fun X.print() = println(x)`
Use like this:
Copy code
Foo.print()
s
maybe? imo it’s not clear what exactly you’re trying to access
n
@karelpeeters great, now try to pass Foo into a generic function and access
x
🙂
d
Have you considered making your companion objects implement an interface that the function uses?
k
You can't really, a type and its companion object are completely distinct as far as the typesystem is concerned (I think)
d
I believe they are distinct as well.
n
So, I was reviewing the Kotlin duration KEEP, and I wanted to see if the C++ duration approach could be duplicated, because it's very slick. It looks like this:
Copy code
template <class T, class Period>
class Duration;
k
(There is always reflection of course)
n
The idea is that Period is a type, but it's not stored inside the duration class. Instead, Period is a type, that let's access the ratio of a "tick" for this duration type
So basically inside Duration, it stores a
T t;
. If you want to get the duration in seconds, you would do
t * Period::ratio;
What's nice about this is that you aren't storing an instance of Period inside the Duration class.
Duration class is just a lightweight wrapper around the storage type, which is typically just a 64 bit integer.
This lets people control whether they want e.g. nanosecond resolution, but a lower time range, or lower resolution with a higher time range.
So obviously this is a class, but I was wondering first if anything like this was possible with functions because they support inline/reified, which classes don't yet, but I've heard will in the future
d
I might address this with a type alias, and then a set of companion objects.
n
So the key point here:
Copy code
inline fun <reified T> foo() : Int {
    return T.bar
}
Is that the function can extract constants from a type without receiving an instance of that type. Since the type is reified, this is clearly possible in principle, the question is just whether Kotlin supports it currently.
@dalexander Can you elaborate?
s
There seems like a number of ways you could provide that constant
d
Well, I would create a
Time
type alias of some variety that used a long for a backing value, then... hm. It should be relatively easy to store the ratios in associated objects as constants.
s
if you’re worried about storing an instance of Period, would storing a reference to a constant be better
d
I think I might be missing some of the type information you're looking for though.
Oh, even better, they could use inline classes.
Copy code
inline class Seconds(val backing: Long) {
  fun toMilliseconds(): Milliseconds = Milliseconds(backing * 1000)
  fun toMinutes(): Seconds = Seconds(backing / 60)
  ...
}

inline class Seconds(val backing: Long) { ... }
Something like this should be possible I think? Inline classes are currently experimental however. That should get a nice interface to work with while also not introducing any performance issues.
m
@dalexander yeah that works 😁 https://github.com/fluidsonic/fluid-time/tree/master/sources/common/measurement I'd try avoiding interfaces and generics though as they involve a lot of boxing and object allocations which isn't good for performance critical/sensitive applications.
n
If you ahve inline classes then it should get rid of that, no
@dalexander That's not really what I'd want to do.
h
A Bit late for the discussion, but maybe this helps: there is no way to require a static value for a type to be present, because interfaces don't contain static things or companion contracts. This is one of the things that keep-87 could deliver.
n
The problem is then you have to write separate Seconds, MilliSeconds, NanoSeconds, MicroSeconds, Days, classes
m
Get rid of what? If you use an inline class through an interface or in a generic context every single time boxing will be involved.
n
and then you have to write overloads or conversion operators to operate with all of them
if they are all instances of the same generic, that's not the case
h
This requirement pops out every now and then, like people want to require static factory methods for types etc
n
@Marc Knaup I see what you're saying. That's a real shame. The problem I guess with
inline
is that unless you use it throughout it doesn't solve the problem.
m
Yeah, such a library is write-once but use-often. So writing all conversions manually gives huge benefits in terms of performance and library users don't see that much of a difference. It's okay to have more work to do upfront here.
n
Well, they see a big difference in that they can't easily add their own types
m
Yup. But how often does that happen? And then again you do it only once. And you could use generics and interfaces. They're just not optimized then.
n
My company are big users of std::chrono in C++, and it's a huge benefit that you can define your own clock/epoch, control the backing type, write your own type aliases, and it still works with all of the API in chrono because it's all generic
So, it happens more than you think.
m
So what was the reason to do that in the first place? Flexibility always comes at a cost.
n
Sorry I'm not sure I follow
The cost is probably making things more complicated for the standard library implementers to implement? There's no performance cost.
m
Why do you want to change the backing field? Clock is easy, it can just be an interface. Type aliases also should be a big problem I Kotlin.
n
But, I understand that because of how Kotlin generics work, they are not "zero overhead" like C++ or Rust. So hardcoded types might be the best choice.
👌 1
@Marc Knaup Because, you want your Duration to be backed by a simple numeric type, have high enough precision, and cover a broad enough range
And these 3 can't be achieved for everyone at the same time
m
Isn't Java's Duration sufficiently precise here?
n
Java's duration isn't backed by a simple numeric type
m
Yeah I know. But you can use it for general purpose and high precision
Everything else which is more performance sensitive could use the specific inline classes
n
Sure. So, that's the trade-off:
Stores value as Long seconds + Int nanoseconds
This actually adds quite a bit of performance penalty; after every time you add two durations you need to see if your nanos have overflowed into seconds for example
However, in Kotlin it seems like generics add a big performance penalty.... so you may as well have "one Duration type to rule them all"
m
Check out my lib. It has Duration but also Milliseconds, Nanoseconds, etc. Just still very bare bones.
h
I don't think this has anything to do with kotlin, but with the jvm, which doesnt have value types
s
What do you mean by generics adding a big performance penalty? Generics are only really resolved at compile time, though
reified
definitely changes things by getting reflection involved
m
Kotlin isn't only about JVM anymore :) And JVM doesn't have inline classes either.
s
the JVM may not have value types but that doesn’t mean you can’t implement a performant Duration type on it
m
@Shawn generics are still type erased to Any. Every time you use an inline class as Any a special box class is instantiated holding the inline value
s
I’m a little confused, when did we start talking about inline classes?
That does suck, I agree with you there
m
Somewhere down the thread and my remarks were a reply to that :)
n
he was discuss with a naive C++ programmer 🙂
m
@Shawn it's the same principles as apply to primitives. Except that there's no optimization as there is for some primitives
n
I still think that if you have zero cost generics then the C++ approach to duration/date/time/chrono is the best, but I can see why if you don't have that, it probably isn't
m
I've also worked a lot with C++ and I miss their generics in Kotlin indeed 😅
n
Hah yeah.... still looking for that perfect language 😉
m
@Nir I agree on that
n
I should write something in the KEEP proposal to this effect. But still insisting that Duration types be integer based, not floating point.
m
I'd also prefer integer. I've always found the approach of Objective-C and Swift annoying.
n
They use double backed Duration 😱
You should comment and link your library in this thread: https://github.com/Kotlin/KEEP/issues/190
m
Yup
I don't think that my lib is in any shape to be a suitable example 😅
n
Well, you can still discuss, make your points, etc, even if it's sample code can still prove a point
m
I did. At the very top of the discussion
n
oh sorry I missed it then
m
Oh, no, that was about naming