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

Nir

07/10/2019, 2:33 PM
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

karelpeeters

07/10/2019, 2:34 PM
No duck typing please!
n

Nir

07/10/2019, 2:36 PM
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

dalexander

07/10/2019, 2:36 PM
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

Ruckus

07/10/2019, 2:37 PM
@Nir You can access anything from the Type, but the Type is not the same as the companion.
n

Nir

07/10/2019, 2:38 PM
I did read about companion objects, and tried various things, but couldn't make it work
Would explaining the use case be helpful?
d

dalexander

07/10/2019, 2:38 PM
It would definitely help 🙂
k

karelpeeters

07/10/2019, 2:38 PM
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

Shawn

07/10/2019, 2:38 PM
maybe? imo it’s not clear what exactly you’re trying to access
n

Nir

07/10/2019, 2:39 PM
@karelpeeters great, now try to pass Foo into a generic function and access
x
🙂
d

dalexander

07/10/2019, 2:39 PM
Have you considered making your companion objects implement an interface that the function uses?
k

karelpeeters

07/10/2019, 2:39 PM
You can't really, a type and its companion object are completely distinct as far as the typesystem is concerned (I think)
d

dalexander

07/10/2019, 2:40 PM
I believe they are distinct as well.
n

Nir

07/10/2019, 2:40 PM
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

karelpeeters

07/10/2019, 2:40 PM
(There is always reflection of course)
n

Nir

07/10/2019, 2:41 PM
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
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.
d

dalexander

07/10/2019, 2:45 PM
I might address this with a type alias, and then a set of companion objects.
n

Nir

07/10/2019, 2:45 PM
@dalexander Can you elaborate?
s

Shawn

07/10/2019, 2:46 PM
There seems like a number of ways you could provide that constant
d

dalexander

07/10/2019, 2:46 PM
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

Shawn

07/10/2019, 2:46 PM
if you’re worried about storing an instance of Period, would storing a reference to a constant be better
d

dalexander

07/10/2019, 2:46 PM
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

Marc Knaup

07/10/2019, 3:05 PM
@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

Nir

07/10/2019, 3:08 PM
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

Hanno

07/10/2019, 3:09 PM
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

Nir

07/10/2019, 3:09 PM
The problem is then you have to write separate Seconds, MilliSeconds, NanoSeconds, MicroSeconds, Days, classes
m

Marc Knaup

07/10/2019, 3:09 PM
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

Nir

07/10/2019, 3:09 PM
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

Hanno

07/10/2019, 3:10 PM
This requirement pops out every now and then, like people want to require static factory methods for types etc
n

Nir

07/10/2019, 3:10 PM
@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

Marc Knaup

07/10/2019, 3:11 PM
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

Nir

07/10/2019, 3:12 PM
Well, they see a big difference in that they can't easily add their own types
m

Marc Knaup

07/10/2019, 3:12 PM
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

Nir

07/10/2019, 3:13 PM
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

Marc Knaup

07/10/2019, 3:13 PM
So what was the reason to do that in the first place? Flexibility always comes at a cost.
n

Nir

07/10/2019, 3:13 PM
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

Marc Knaup

07/10/2019, 3:14 PM
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

Nir

07/10/2019, 3:14 PM
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

Marc Knaup

07/10/2019, 3:15 PM
Isn't Java's Duration sufficiently precise here?
n

Nir

07/10/2019, 3:16 PM
Java's duration isn't backed by a simple numeric type
m

Marc Knaup

07/10/2019, 3:16 PM
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

Nir

07/10/2019, 3:16 PM
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

Marc Knaup

07/10/2019, 3:18 PM
Check out my lib. It has Duration but also Milliseconds, Nanoseconds, etc. Just still very bare bones.
h

Hanno

07/10/2019, 3:19 PM
I don't think this has anything to do with kotlin, but with the jvm, which doesnt have value types
s

Shawn

07/10/2019, 3:21 PM
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

Marc Knaup

07/10/2019, 3:21 PM
Kotlin isn't only about JVM anymore :) And JVM doesn't have inline classes either.
s

Shawn

07/10/2019, 3:22 PM
the JVM may not have value types but that doesn’t mean you can’t implement a performant Duration type on it
m

Marc Knaup

07/10/2019, 3:22 PM
@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

Shawn

07/10/2019, 3:23 PM
I’m a little confused, when did we start talking about inline classes?
That does suck, I agree with you there
m

Marc Knaup

07/10/2019, 3:24 PM
Somewhere down the thread and my remarks were a reply to that :)
n

Nir

07/10/2019, 3:24 PM
he was discuss with a naive C++ programmer 🙂
m

Marc Knaup

07/10/2019, 3:24 PM
@Shawn it's the same principles as apply to primitives. Except that there's no optimization as there is for some primitives
n

Nir

07/10/2019, 3:25 PM
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

Marc Knaup

07/10/2019, 3:25 PM
I've also worked a lot with C++ and I miss their generics in Kotlin indeed 😅
n

Nir

07/10/2019, 3:25 PM
Hah yeah.... still looking for that perfect language 😉
m

Marc Knaup

07/10/2019, 3:25 PM
@Nir I agree on that
n

Nir

07/10/2019, 3:26 PM
I should write something in the KEEP proposal to this effect. But still insisting that Duration types be integer based, not floating point.
m

Marc Knaup

07/10/2019, 3:26 PM
I'd also prefer integer. I've always found the approach of Objective-C and Swift annoying.
n

Nir

07/10/2019, 3:27 PM
They use double backed Duration 😱
You should comment and link your library in this thread: https://github.com/Kotlin/KEEP/issues/190
m

Marc Knaup

07/10/2019, 3:27 PM
Yup
I don't think that my lib is in any shape to be a suitable example 😅
n

Nir

07/10/2019, 3:30 PM
Well, you can still discuss, make your points, etc, even if it's sample code can still prove a point
m

Marc Knaup

07/10/2019, 3:30 PM
I did. At the very top of the discussion
n

Nir

07/10/2019, 3:30 PM
oh sorry I missed it then
m

Marc Knaup

07/10/2019, 3:30 PM
Oh, no, that was about naming