7 ms·
All that, and yet I don't see a single example of what would be "correct", or an example language that does it better. This just comes off as poorly thought out
by svnpenn 5y ago
All that, and yet I don't see a single example of what would be "correct", or an example language that does it better. This just comes off as poorly thought out rant.
In my opinion, it is well designed. First of all, who is multiplying time.Duration against itself? I've been programming Go for a few years basically every day, and I've only ever seen the package constants used by themselves, or with untyped constant. I think it's a great syntax, better than any example in the article, as you don't have mystery numbers.
- manwe150 5y agoJust contrived examples, like gravity * t^2 to get distance to fall and such, probably
- bobbylarrybobby 5y agoWhat is correct is that duration ± duration = duration, duration * scalar = duration, timestamp ± duration = timestamp, timestamp - timestamp = duration, and anything else doesn't compile.
- Groxx 5y agoTo call it out specifically: this does not include `duration * duration = duration` Go is currently allowing that, which makes `delaySecs * time.Second` a billion times larger than it appears to intend. I've personally run across code that has this kind of flaw in it... at least several dozen times. It's the kind of thing that's only noticed when it misbehaves visibly while someone is watching it. (I read a lot of other-teams' code, which is in various states of quality and disarray)
- chrismorgan 5y agoAnd it’s not just that Go allows that, but that that’s actually the only general way of doing it, as Go only allows duration * scalar in constant context (which is admittedly all most people do with durations, which is why I say it’s still probably better than Go did it this way, given their deliberately limited type system).
- svnpenn 5y agoIf you exponent time.Duration, that's a user error. What you're suggesting would be like a compiler error for multiplying two int64s
- conradludgate 5y agoIt should be fine to multiply two durations, but it should not return the same time.Duration type
- linkdd 5y agoWhat they're suggesting is that `time.Duration` should not be an int64. 1 second * 1 second = 1 second² 1 meter * 1 meter = 1 meter² 1 meter / 1 second = 1 m/s Those are not user errors, those are physical values. A physical value is two things: - a scalar (int64, float, ...) - a unit (meter, second, inches, ...) If the type system of your programming language does not allow you to define units, this should at least be a structure with a scalar and an enum, and functions to cast from one unit to another (if possible). Working with units is a common thing in science.
- skissane 5y ago> A physical value is two things: I would argue it is actually three things: a scalar, a unit, and an indication of error – which is at least another scalar, but there are multiple ways of expressing error, so it might require more than just a single scalar (such as an interval and the probability the actual value lies within that interval.) > If the type system of your programming language does not allow you to define units, this should at least be a structure with a scalar and an enum Ideally more than just an enum – Newton = kg*m*s^-2 (equivalently kg^1*m^1*s^-2) – which suggests a set of pairs (unit and exponent).
- linkdd 5y agoYes, thank you for the precisions, this just make my point stronger: int64/float are very ill-suited to represent such values. And it's especially true for time units. For example, "how many seconds is one month?" does not make sense, but "how many seconds is january/february/march?" does make sense. The unit "month" does not really exist, each calendar month is its own unit. And "february" is not even a "stable" unit because sometimes it's 28 days, sometimes it's 29 days. Even a minute can some rare times be 61 seconds. This is why in physics, we use seconds multiplied by powers of 10 and nothing else. To my knowledge, there is not a single programming language that differentiate a "scalar" and a "quantity" (scalar, unit, error).
- afiori 5y agonot being able to sum timestamps is a bit beyond the scope of units, it is more of a vector vs point distinction
- thwarted 5y agoThe provided example is pretty rough, and I'm sure it's occurred in the wild. Sure, put the units in the variable name, and it encourages this kind of mistake, because time.Duration is not "seconds", it is a duration. The variable name should match the API of time.Sleep, which takes a duration. The variable should be named delay. A variable named delaySecs is the same kind of maintenance headache as a variable named "two_days_ago = 2.days.ago" in ruby. The variables used for accepting and parsing input are the ones that should have units in them in this example. Although if you need a delay specified, it's valuable to be explicit and robust in the input and accept a string time.ParseDuration understands. Then you don't have this units problem in your variable naming at all, allows easier input of wider ranges of values by the operator, and makes input validation (if only a subset of durations are allowed) more concise and consistent.
- conradludgate 5y agoI've seen a lot of code at my last job in Go where all duration variables included their units. It was amazingly bad Go code (it was built in part of the projects PoC by new Go devs) but it doesn't help that for the most part it did work. Time was honestly our biggest source of bugs by far. Although adding time.Time ended up being more problematic than durations which were mostly only constructed like that in tests
- tedunangst 5y agoOne alternative is rust Duration, which makes you spell out all your arithmetic operations. But this is exactly the approach taken by go time.Time, so the part about the type system being incapable or whatever is kinda misguided. https://doc.rust-lang.org/std/time/struct.Duration.html https://doc.rust-lang.org/std/time/struct.Duration.html C++ chrono::duration allows arithmetic operations with more sensible overloading. https://en.cppreference.com/w/cpp/chrono/duration https://en.cppreference.com/w/cpp/chrono/duration
- chrismorgan 5y agoOnce you go beyond constants (which in practice means literals, I think, but I’m not conversant enough in Go to be confident), Go requires that you multiply Duration by Duration—you can’t multiply it with a scalar outside of constants. In other words, as soon as a scalar multiple becomes a parameter rather than a constant, you can’t just do `n * duration`, but have to do something more like `Duration(n) * duration`, which is obviously physically wrong for a system of units, because the unit should be time squared, not time. As for languages doing it better, approximately every single language that has strong static typing and uses dedicated types for times does it better. Rust is the one I’m most familiar with and comfortable with.
- svnpenn 5y agoYou seem to be confusing the Duration type with Duration values. Yes, if you insist on using a typed value, instead of an untyped constant, then that value needs to be type Duration. But Duration(1) is way different than time.Second. honestly it just sounds like you don't know the language, and aren't willing to learn it. Go is not Rust. Things are different, that doesn't mean Go sucks.
- chrismorgan 5y agoThe entire purpose of the distinction that I’m remarking on is that Duration * Duration → Duration is mathematically utterly incorrect, and especially super misleading when the base quantity for the unit is nanoseconds rather than seconds, yet that is what Go requires, beyond constants, which it special-cases. To be sure, with durations, the distinction doesn’t matter much because arithmetic performed is with constants, and that’s why I say that Go is probably still better with this wonky unit scheme than with entirely unitless quantities, but there are plenty of situations where you will want to multiply durations by typed numbers, and so you’re forced to do the mathematically-ridiculous `Duration(n) * duration` rather than `n * duration`.
- svnpenn 5y agoI'm sorry, but I just don't think people use code like you're describing. In your mind, you see this: hello := 2 hello * time.Second // oh no But people actually use code like this: hello := time.Second hello *=2 People working with Go code (who know what they're doing), don't declare an int, only to immediately cast it to something else.
- xigoi 5y agoNim does it correctly: https://nim-lang.org/docs/times.html#%2A%2CDuration%2Cint64 https://nim-lang.org/docs/times.html#%2A%2CDuration%2Cint64
- int_19h 5y ago> All that, and yet I don't see a single example of what would be "correct", or an example language that does it better. F#, Rust, C++?.. > First of all, who is multiplying time.Duration against itself Physicists do, every time they have to deal with acceleration - m/s^2.
- WinterMount223 5y agoYou are right however physicists do not multiply duration x duration but time x time. Duration is discrete and time is infinitesimal.