-
-
Notifications
You must be signed in to change notification settings - Fork 416
core.time: Conversions and documentation #711
Conversation
Here is an example: TickDuration.seconds will return the total number of seconds in the TickDuration. However, Duration.seconds will return the number of seconds in the Duration minus the larger units. WTF! |
Yeah, the naming could be better. One meaning is the total number of seconds and the other is seconds as fraction of a minute. How can we improve the method names? |
|
While preserving backwards compatibility? I'm not sure. If I were to write the module from scratch, what I'd do is:
I suppose that everything is possible with a deprecation path, but the changes would not be minor. |
|
|
It's primary use is for |
| this(TickDuration td) | ||
| { | ||
| _hnsecs = td.hnsecs; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the point of adding these constructors over simply casting like you'd do now? I don't see how this adds anything.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Casting should not be used as a mechanism for "safe" conversions between types. cast is a keyword in D specifically to aid code review to find places where something unsafe might be going on. I do not believe that overriding opCast for safe semantic conversions between types is in the spirit of D. It is also too much syntactic baggage for an operation as mundane as converting between these two similar types, and unlike e.g. .fracSec, you do not need to remember any property/method names to perform the conversion - just the type you want to convert to.
Using a constructor is already an established convention for conversion between types. We are still missing the int(x) syntax in the language for implicit casts to basic types, and I believe Andrei mentioned that he would be in favor of adding it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, casting easily looses the correct qualifier, which is not a problem here, because those are all value types.
Still it's a weird idiom for normal conversion because in general cast allow to do unsafe conversions.
Using a constructor is already an established convention for conversion between types. We are still missing the int(x)
Yep, it's scheduled for the next release and definitely necessary for generic programming.
http://wiki.dlang.org/Agenda#introduce_.22uniform_construction.22_syntax
Interesting side note, in C++ int(x) is actually a blunt C-style cast.
http://stackoverflow.com/questions/4474933/what-exactly-is-or-was-the-purpose-of-c-function-style-casts
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you don't like casting, then use std.conv.to. opCast is the standard way to define conversions between types.
Regardless, if we're going to introduce different conversions here, I'd be more inclined to create an alias this on TickDuration to implicitly convert to Duration and then not do anything about converting from Duration to TickDuration (and just leave the cast), since I don't think that converting from Duration to TickDuration makes sense under very many circumstances. You use TickDuration, because you need the high precision clock, and then you generally try and get the time out of a TickDuration - either by converting it to a Duration or calling one of its properties to convert it to seconds or milliseconds or whatever. At most, you keep it around to compare to other TickDurations. I don't think that much code is going to be interesting in convert a Duration into TickDuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you don't like casting, then use
std.conv.to.
I don't think converting between these types should require a dependency on std.conv. It's great that std.conv can convert to it, as it aids generic code, but IMO it shouldn't be the only way (save for casting directly).
and then not do anything about converting from
DurationtoTickDuration(and just leave the cast), since I don't think that converting from Duration to TickDuration makes sense under very many circumstances.
I use Duration -> TickDuration conversions a LOT in my code. Why:
- The timer subsystem refers to the monotonic clock, and thus uses
TickDurationinternally. - When you create a timer, you need to specify the duration as a
TickDuration, because the interval you want the timer to have may come from the timer subsystem (and thus, using aDurationwould involve a double conversion). - When you create a timer, most of the time you want to specify the interval as a duration (e.g.
5.seconds).
There definitely needs to be an easy way to convert from Duration to TickDuration.
|
@jmdavis Regarding |
|
And regarding
Programs still need a way to query high-resolution / monotonic clocks, whereas |
I certainly wouldn't suggest using We're currently trying to use So, we'd end up with something like The high precision, monotonic clock would be there with |
|
Sounds like you have some solid ideas for
Until now we've always found acceptable solutions for deprecation after thinking hard enough about it, so I'm quite optimistic here too :). |
|
Good ideas. I agree that BTW, why wasn't |
|
Regarding this pull request, the documentation overview is a huge improvement. |
| Returns a $(LREF TickDuration) with the same number of hnsecs as this | ||
| $(D Duration). | ||
| +/ | ||
| // To be used with std.conv.to. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure that std.conv.to can't use constructors too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It might. It's also possible that it couldn't when this was written. Regardless, it can use opCast, and std.conv.to is pretty much the way to convert one type to another (especially because it tends to work much better in generic code), so that's what I would expect most code to be using anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it can. In fact, it had a bug where if the source type had opCast and the target had a constructor, there would be an ambiguity (this is dlang/phobos#1864 that this pull depends on). I guess opCast now only needs to stay to avoid breaking existing code that casts directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then replace the comment with // explicitly undocumented ... will be deprecated ..., that is if you two agree on this step.
I've been debating it for a while but haven't done anything yet due to a combination of a lack of free time and a concern about code breakage. But I'm increasingly inclined to just do it and deal with the code breakage. Having both
It was to distinguish it from I suppose that I could have called it |
|
I see. I'd forgotten about |
I agree that the documentation improvement is great. I just object to the addition of the constructors, because |
They're ideal when you need to manipulate all of the individual properties ( |
|
Well, what bugs me about the What would I put in the conversion table? From a practical point, I would also be happy with replacing the constructors with |
I honestly question forgoing Phobos so much that you don't even use std.conv.to (particularly since it's pretty much the way to do conversions in D), but in that case, I'd argue for just casting. And with we introduce
That's fine with me. Either that or list them as casts with a note that you can use std.conv.to instead of casts if you don't want to use casts. For
I don't think that we want to introduce an |
|
Okay. I opened an enhancement request on the https://d.puremagic.com/issues/show_bug.cgi?id=11989 And wow are those issue numbers getting high. |
Well, my experience shows otherwise on this point, as I mentioned above. I don't think my use case is unthinkably rare. Why do you think this conversion is so unlikely as to be an exception in practice? Anyway, as there is no consensus I will probably remove the constructors from this pull request, and perhaps create a new one once the uniform construction syntax is implemented. However, I must insist on either undocumenting |
I would expect the two primary cases for If you're using it to get the monotonic time, I'd expect that the only needs for conversion would be to convert to If you're using In none of those cases is there a conversion from
Feel free to improve upon opCast's documentation. I try and write good documentation, but much of what's here is now several years old, and I'm not necessarily in tune with what other people are or aren't going to understand without further explanation. What you've added to the module documentation is certainly quite nice. |
I described it here: #711 (comment) Edit: also, |
Oh sorry about that. I'd forgotten about that comment. It sounds like the problem is really the timer subsystem in that it should take a
Yeah, that probably not so good given that you normally add a In any case, I'd favor adding an implicit conversion from |
OK, I'm going to try changing all my timing code to use |
I don't think we can do that at this point, as the |
My thinking was that if there's an |
Adding a |
OK, I've returned with some RealWorld™ results. I've actually went ahead and implemented a mock
As for this pull request: I've removed the new constructors, and simply undocumented |
The solution to that IMHO is just to not do any conversions with floating point values at all. Floating point types really have no business in time-related operations as they introduce rounding errors. If only integral operations are used, then the conversion should be consistent, and
It shouldn't be. That's a bug in the documentation. I'd have to look at it to see best how to adjust it though. I'll try and get to that later.
The docs at the top are good, but I think that |
Sorry if I used any terminology improperly, but I did not mean to imply floating points. Consider: On my system, the monotonic clock resolution is 3_515_654. |
Well, I don't think it should be documented because we do not provide it as functionality to the user. It is a hook for std.conv and nothing else, isn't it? So I think the reason for not documenting it is the same as the reason for why nothing in the |
Hmmm, I was thinking that you could get away with it, but you're right. It's bound to be off just slightly at least some of the time. I had a little bit of time this evening, and I figured that writing I'm going to have to think about this. It seems off to me for the monotonic clock to have its internal time in hnsecs rather than the actual monotonic time, which leads me to consider having it contain both, but that opens the question of when to use hnsecs and when to use ticks. Maybe two |
The user is free to use Documenting that |
|
Ah, good points. I'll add them back. |
|
Done. |
|
Auto-merge toggled on |
|
I've just realized a potential problem with my idea of converting the monotonic clock eagerly. What happens if it overflows? A pair of pre- and post-overflow values, converted to something else (like hnsecs), and subtracted one from another, are going to give wildly inaccurate results. However, I don't think this is a problem in practice on any of the monotonic clocks we use, as they have enough bits to not overflow for a long time. Monotonic clocks which might overflow exist, e.g. GetTickCount on Windows, but we aren't using them. |
|
@CyberShadow I'm considering going for having The greatest resolution that I would expect at this point would be nanoseconds, which gives over 200 years worth of ticks. So, just so long as the monotonic clock doesn't start well into those 200 years, we should be fine. On Linux, it appears to start at 0 when the computer boots up. I don't know what other OSes do though. However, I don't see an easy way to deal with overflow if it happens. It can be done (you have to do pretty much just that with stuff like RTP sequence numbers), but you start having to use heuristics to figure out whether you've overflowed or not, and it's not something that I'd want to do if we can avoid it. But even if we were using the native resolution directly, we'd have the same overflow problem if overflow can occur, and any conversion problems that we'd have converting to hnsecs would occur whenever we subtracted two |
core.time: Conversions and documentation
|
What's Anyway, my gut says that there might still a catch somewhere with your suggestion, but I can't lay it down on paper without some code to work with (and there is still enough ambiguity with e.g. rounding and conversions that I can't implement it myself knowing that's what you meant too). So I guess let me know when you have a PoC implementation. |
Well, you get some amount of rounding error when you do the conversion from system ticks to hnsecs or nsecs or whatever the resolution for However, by doing the conversion only once (by having But yeah, I'll have to finish it and see what issues other folks find with the design. I could very well be missing something. |
|
Thanks for making this pull, I'm in the same boat getting confused with timing in D. |
|
Hmm, it seems that the example is going to fail unless sigh More food for thought, I guess. The basic solution here is so simple, but some of the ramifications definitely aren't. I guess that I'll have to chew on this some more. I'd kind of like to bring it up in the newsgroup for discussion, but I think that I need a definitive proposal to be critiqued for that to work well. Otherwise, I fear that there would be too much bikeshedding and too little of value in the discussion. I feel like I'm so close and yet so far. Oh, well. Back to chewing on it, I guess. |
|
You could make subtraction return a |
|
@MartinNowak Yeah. That could be one way to go about it. However, that does move us towards having two duration types again. It wouldn't be quite the same - especially if Definitely a good suggestion, but it would have to be done in a way that gave just enough functionality to do what we need without getting overly complicated or causing confusion. The implicit conversion to |
|
Voldemort would work if you had multiple alias this, i.e. multiple implicit conversion targets, but that is far from implemented. |
I got absolutely fed up with having to check core.time's documentation and scrolling through pages of fluff to remember its inconsistent/obtuse conversion syntax, and I decided to fix it.
You now can convert betweenDurationandTickDurationby using constructors (Duration(tickDuration)and vice-versa). Isn't this the proper way that all value types in D ought to convert between one another?Here's what the new module documentation looks like:
Needs dlang/phobos#1864.