-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
APIs should have typesafe self-documenting time units, eg proc sleep(t: Duration)
, allowing sleep(1.millisecond)
etc
#383
Comments
proc sleep(t: Duration)
, allowing sleep(1.millisecond) etc
proc sleep(t: Duration)
, allowing sleep(1.millisecond) etcproc sleep(t: Duration)
, allowing sleep(1.millisecond)
etc
Not that I disagree with the RFC, but it's getting tiresome to read things like:
You know perfectly well that APIs are inconsistent because they were written at different times by different people. And that wrappers are more restricted than idiomatic Nim code. I don't write
Exactly. Because code was not written after every eventuality has been considered and the solution has been compared to the 100 existing, similar solutions. Code was written, "hey it works for my project and it's generally useful" and then submitted to the stdlib. IIRC you sometimes do the same thing... ;-) New code is added all the time that is designed more carefully and it's a good thing. So yeah, I completely agree with this RFC, I just wish it would avoid the constant punching at we already have. Last time I checked, people called |
I think you're reading too much between the lines, this RFC isn't meant as a criticism of some APIs (even less so of their authors), its only goal is: today, pattern B is better than pattern A (because of the reasons given above eg The other aspect (more controversial) is that best practices should be preferred over consistency with surrounding code, otherwise silos persist.
that's the reason for such RFCs, discuss/establish/update existing best practices to make them easier to follow
yes; this RFC doesn't cover thin wrappers like are duration suffixes really needed?
Actually I'm not sure we need numeric suffixes here unless I'm missing something. The problem with suffixes here is that if you add: # in std/durations:
proc `'millisecond`*(a: string): Duration =
# in pseudo-code
try: # handle 1234'second, avoiding loosing FP conversion accuracy issues
result = toDuration(a.parseInt, kSecond)
except ValueError: # handle 1.5'second
result = toDuration(a.parseFloat, kSecond) you'd still need to allow conversions from int|float, eg: let t = 1000
let t2 = t.second so it doesnt' really resolve anything |
I wonder if a |
I've implemented runnableExamples:
type
Length = "m".makeUnit # can be also defined from other powers of other units
Time = "s".makeUnit
Mass = "kg".makeUnit
assert $(Time(1.2)/Length(3.2)*Mass(5.4)*Mass(5.4)) == "10.935's*1/m*kg²" but that'd require a separate RFC and isn't needed for this RFC (in particular, durations should be integral, not FP, whereas units are more useful as FP in most applications)
what do you mean twice? the conversion should be 1-way and propagated throughout stdlib until the point where it's consumed by final OS call into the unit it needs; the goal would be to only consume Duration through and through. The only problem I'm facing to implement this is that I'm not sure the implementation of type Duration* = object
seconds: int64
nanosecond: range[0..999_999_999] which doesn't lend itself for efficient operations. So I'm instead considering introducing a different type to represent durations that doesn't incur performance penalty, eg: # in system:
type int128* {.magic: Int128.}
# in C backend, uses `__int128` if `#ifdef __SIZEOF_INT128__` is defined,
# else falls back to software emulation as implemented in compiler/int128.nim
# in VM, uses compiler/int128.nim
# in js, uses jsbigints or a wrapper around it if we care about preserving bounds checking
# in std/durations:
type TimeUnit* = distinct int128
proc second*(a: int | float): TimeUnit = ... # now the math is simple and efficient The name The alternative would be to use int64 but it can "only" represent ranges +- 292 years in nanoseconds, which can be a problem if doing math involving calendar year (eg 2021) + offset operations. notestd/bigints would not be a viable replacement for int128, for 2 reasons:
ie, both should be added to stdlib int would still be int64 even if we added int128 (as for BiggestInt, I'm not sure, TBD) => #399 |
Do it. |
https://github.com/SciNim/Unchained I'm obviously a fan of this, but not sure if it should be a standard library feature to be honest. |
I think the distinction between I believe OSes going to Nim is not as constrained as the C world and this already talks of specifying units in API calls. I think that strongly motivates a dense valued I also think I'm just trying to help here. I think there is a useful simplification/clean-up of times to be had, though date & time modules are pretty cursed. |
I don't think being burned by 2038/etc should generate an irrational fear to provision calculation "beyond the heat death of the universe to the nanosecond". :-) Why, Gregorian calendar adoption itself is only barely outside -292 years and we've been adding more and more "leap nanoseconds" as well (as tides slow the Earth's rotation). To the extent crazy long-term calendar math does happen, those programmers have (or should have) their own 20 line of code library with full Julian-Gregorian calendar conversion. This is what astronomers do. They calculate in units of days not dates, use the Julian Calendar, and then convert, possibly splitting day & time of day calculations if they think they can get some calculation down to minute-accuracy. I don't see why this isn't a fine solution for people with "astronomer like time ranges". In fact, I would expect them to expect to need it, not rely on any prog.lang's stdlib time module. :-) |
as noted in nim-lang/Nim#17149 (comment),
sleep
is confusing:cint
)int
)this is confusing, not self documenting, error prone and not particularly typesafe
more generally, plenty of APIs have the same problem, where you "just have to know" and it's unclear what the unit is especially at callsite, eg:
proc suspend(sleepTime: float = 0)
,proc wait(c: CoroutineRef; interval = 0.01)
,proc withTimeout[T](fut: Future[T]; timeout: int): owned(Future[bool])
=> sometimes it's seconds, sometimes milleseconds (can you guess?)
proposal
proc sleep(t: Duration)
example:
int
(orfloat
) with some implied unit (eg seconds, milliseconds etc), add an overload that takes aDuration
and simplify its documentation which then doesn't need to specify the unitDuration
params, eg in Fix for #18161 AsyncHttpServer Issue: Too many open files, against devel branch. Nim#18205, instead ofkeepaliveTimeout = 3600
, it should be:keepaliveTimeout = 3600.second
(or equivalently, 1.hour)note:
std/durations
should be split off fromstd/times
std/times is a heavy dependency, ideally we'd be able to do this without depending on std/times, refs #55, via a new std/durations module (imported and re-exported by std/times); this can be done independently of this RFC
note: Duration, not TimeInterval
TimeInterval
is not good, this has overhead because it's calendar based and it's the wrong abstraction for expressing durations anyways.we can define
millisecond
,second
(etc) withouts
to avoid confusion with the existingmilliseconds
+ friends which are (rather sadly) returningTimeInterval
(eg https://nim-lang.github.io/Nim/times.html#milliseconds%2Cint); there is no confusion though, we're not usings
.eg:
note: thin wrappers (eg posix.sleep) are excluded from this RFC
links
proc sleep(t: Duration)
, allowingsleep(1.second)
,sleep(1.millisecond)
etc timotheecour/Nim#617The text was updated successfully, but these errors were encountered: