Please put units in names
ruudvanasseldonk.com
ruudvanasseldonk.com
Something like:
unit type Meters = m
unit type Seconds = s
function sleep(time: Int[s]): void
val speed = 5.4 m/s // Type = Float[m/s]
val distance = parseFloat(prompt('enter time')) * 1 m // Convert unitless to meters just by multiplying
val time = distance / speed // Type = Float[s]
print("${time}") // Prints "# s"
print("${time / 1 s} seconds") // Prints "# seconds"
val complexUnit = 7 * 1 lbf/in2 // 7 lbf/in2
// != 7 psi (too hard to infer) but you can write a converter function
function toPsi<N : Numeral>(value: N[lbf/in2]): N[psi] {
return value * 1 psi*in2/lbf
}
It requires extending number parsing, type parsing (if the brackets aren't already part e.g. in TypeScript), and extending types to support units of measurement at least if they are statically known to be subtypes of `Numeral`.Naming variables with their units doesn't solve the issue of mis-casting and using units incorrectly, and newtypes are too inconvenient (and sometimes impossible without affecting performance) so nobody uses them. Even as a very software-focused programmer I encounter units like seconds, bytes, pixels, etc. all the time, they are almost never newtypes, and I get bugs from forgetting to convert or converting incorrectly.
Even though branded types prevent assignment of values with invalid units, opaque types are inferior in that the compiler doesn't know the relationship between the types (as exemplified in the parent comment) and you need a slew of helper functions & casting.
See std::duration/time_point specifically for times, and boost.units for generalized unit support.
It is implemented via (templated) wrappers, but they are very close to zero overhead if not completely free. User defined literals also allow for a very succinct syntax, but to be honest, I usually don't bother. I normally write:
auto delay = std::chrono::seconds{3};
instead of: auto delay = 1s;It is really nice to have a type system that checks your formulas. Saved me a couple of times.
F# does if you tell it, i.e.:
[<Measure>] type kg = g
[<Measure>] type m
[<Measure>] type s
[<Measure>] type N = kg * m / s^2
Although, of course, in practice you'd probably just use whatever's defined in https://fsharp.github.io/fsharp-core-docs/reference/fsharp-d...Not for log units tho.
I understand that the speed variable is automatically assigned a type of Float[m/s] based on the m/s there, but I'm confused about how the units are just placed at the end of the value assignment: val speed = {value} {unit}
Is this just a F# property that units can be added at the end of value assignment, and F# interprets them as units correctly?
Also, anyone know of methods for incorporating units as types in Python? It would be great to have an equivalent method as demonstrated here, where dividing types automatically creates a new type unit, and perhaps even associates with a related type, i.e: type(kg / (m * m * m)) == density
Unfortunately the same isn't as easily done in Python:
1. You would need to reify these units as actual constants with overridden operators to track values, and some wacky (if even possible) mypy type shenanigans.
2. The types aren't erased and so would impact the performance of any critical code. F# isn't quite used in high frequency trading, but it does target numeric heavy users like those wanting to write scientific workloads, and units help improve correctness there. But if the types are computed at runtime and not erased, it would be an enormous hit to performance.
Unless you can figure out some way to get "3.0 * m" to be a plain old datatype (float) in Python while still retaining type info in mypy/pylance/pyrite. Perhaps there's a way, but I'm not sure.
Edit: This Python library seems to be trying to solve for these issues! https://pint.readthedocs.io/en/0.10.1/
I'm not sure if they've actually solved the runtime performance problem, but I suppose as long as you're not doing unit assignment/conversion in a loop, it should be fine.
val distance = parseFloat(prompt('enter time')) * 1 m // Convert unitless to meters just by multiplyingSo you need a typescript plugin - however the typescript compiler api (which btw is not officially supported) is not flexible enough to support new type syntax (for things like m/s) so you'd a fork.
The documentation read "speed_kmph - This field contains the travelling speed in MILES PER HOUR - please do not be confused by the name".
Woo, great job guys.
I find it really hard to accept any excuse for something like that. It's why enterprise code is so sloppy, people doing the most expedient thing rather than what is correct.
It's hard to know what assumptions code is making and by forcing you to change the code everywhere (which should be pretty easy in any modern IDE) you at least have a chance to evaluate any issues that unit change could cause.
I do agree that generally it's better to use something like a struct that's more flexible, but doing that for every function is also quite verbose. For some functions the time requirement may never change as well.
delay(duration = Duration.ofMinutes(minutes = 1))
which is equivalent to delay(timeMillis = 60_000)
Using the optional argument names here for clarity; you don't have to of course.Sticking with the JVM, a frequent source of confusion is the epoch. Is it in millis or in seconds? With 32 bit integers that has to be seconds (which will set you up for the year 2038 problem). However, Java always used 64 bit longs for tracking the epoch in milliseconds instead even before the year 2000 problem was still a thing. Knowing which you are dealing with is kind of relevant in a lot of places; especially when interfacing with code or APIs written in other languages with more 32 bit legacy.
Things like network timeouts are usually in milliseconds. But how do you know this for sure? Specifying, them in seconds means that there's less risk of forgetting a 0 or something like that. So, using seconds is pretty common too. You can't just blindly assume one or the other. Even if you use the Duration class above, you'd still want to put the magic number it takes as a parameter in some configuration property or constant. Those need good names too and they should include the unit.
For example, in Erlang, the sleep function could/should have been written to take named tuples: `timer:sleep({seconds, 3})`
time.Sleep(10 * time.Millisecond)
To me Durations are a zero sum gain, because in this case they hide relevant detail (int64 nanoseconds) without enforcing usage. Compare it against what Golang could easily provide -- time.SleepMilli(100), which is 40% less characters for my aging eyes to parse than time.Sleep(100 * time.Millisecond).
After all, in the very same package we have a unit in the name:
t := time.Time{}
t.UnixMilli() // 1647952024456 timeout = timedelta(seconds=300)
frobnicate(timeout)
Working with GCP or Azure's Python SDK is like navigating a jungle of types. Some calls return a `compute_engine_list_item` while others return a `compute_engine` type and these are difficult to inspect and reason about, because Python classes default to printing something along the lines of `<__main__.myclass at 0x7fa8864a1040>`, making heavily typed Python code quite difficult to work with.No paradigm is going to save you from spaghetti-code, but being able to pass a list and get an integer in return, makes it very easy to reason about what you can do with these values, whereas it can be quite difficult to reason about custom types/objects.
My point is, that knowing if `frobnicate(timeout=300)` is in seconds or minutes can be just as difficult (or even more difficult) as knowing what specific object I need to instantiate and pass to `frobnicate` (in the above case a `timedelta`)
I disagree; to paraphrase Ian Malcolm: what you can do with these values is less important than what you should do with these values. For example, we can add a distance to a currency, if they're both int or float; that doesn't mean we should.
The most obvious example of this is "stringly-typed programming", where pretty much everything is "string". Can I append a user-input string to an SQL statement string? Sure; but I shouldn't. Can I append a UserInput to an SQLStatement? Not without conversion (i.e. escaping)!
In this specific example i disagree, timedelta is part of the standard library and should be widely known and familiar.
But in your example above, typed Python would instruct your editor and tools like mypy to flag any improper use of frobnicate. You can set up your editor to offer you a tip on what type to use as soon as you type in `frobnicate(`.
In cases where typing is not really available, I prefer to put types into APIs instead of variable names (and Python makes that great: `frobnicate(duration_as_timedelta=timeout)`).
When possible, I also encourage the use of better types than simple integer values, (like TimeSpan if .NET) as these further reduce ambiguity and the potential for mistakes.
This is such a simple thing to find and fix but it definitely helps in the long term.
In a dynamic language, maybe.
In a static language, especially Haskell/Scala/Ocaml/F#, I hate duplicating the type.
Having everything work this way makes it so much easier to review code for errors. Without this means that as a reviewer you must either trust that the units and conversions are correct or you should do some spelunking to make sure that the inputs and outputs are all in the right units.
The only people I've ever met that think this is unnecessary are also the same people that ship a lot of bugs for other people to fix.
I find feelings about this depend a lot on how much ceremony the type system involves; and just how many units and references to them there are in the system.
Asking bash coders to write "sleep 5s" instead of "sleep 5" - I doubt you'd get any objections at all.
But if you're putting a foo.bar.Duration on a getDefaultTimeoutFromEnvironment on a setConnectTimeout on a HttpRequest on a HttpRequestInitializer on a NetHttpTransport on a ProviderCredential to make a simple get request? People who've come from less ceremony-heavy languages might feel less productive, despite producing 10x the lines of code.
I may be misunderstanding this.
Yeah, you heard me Scala.
Had one making me pull my hair put the other day in C#. C# datetimed are measured in increments of 100 nanoseconds elapsed since January 1st 1 AD or something like that. Was trying to convert Unix time in milliseconds to a C# datetime and didn't realize they were using different units. My fault for not reading the docs but having it in the name would have saved me a lot of trouble.
https://package.elm-lang.org/packages/ianmackenzie/elm-units...
Even if you don’t work in Elm take a moment to look at it.
Why would your type system have encoded unit for kilo-pascal, but not hecto-pascal, mega-pascal, micro-pascal etc?
If you only encode base units (e.g. seconds), then we should use exact-precision arithmetic instead of f32 or f64, which is sometimes an overkill.
If encoding all the modulos (kilo/milli/mega etc) I feel like there are some units may have name clashes (e.g. "Gy" -- is it giga-years, or gray)?
Should we encode only SI units, or pounds/ounces/pints as well?
I think the practical problem stems from widespread APIs which are not communicating their units, and that's what we should be fixing instead: if APIs are clearer (and also self-documenting more), the risks you talk of rarely exist other than in badly structured code.
Basically, instead of having `sleep(wait_in_seconds)` one should have `sleep_for_seconds(wait_time)` or even `sleep(duration_in_seconds=wait_time)` if your language allows that.
But certainly use of proper semantic types would be a net win, but they usually lose out in the convenience of typing them out (and sometimes constructing them if they don't already exist in your language).
It can convert between equivalent units (e.g., centimeters and kilometers), and will complain if you try to add quantities that aren't commensurate (e.g., grams and seconds).
The nice thing about this is that you can write functions that expect unit-ful quantities, and all the conversions will be done automatically for you. And if someone passes an incorrect unit, the system will automatically spit out an error.
But types are not just useful to specify the content of a variable, they are also useful to specify the required precision.
So, if there is a type for seconds in the type system, should it be a 32 bits int or a 64 bits float? Only the user can say.
PIXELS_PER_INCH or BITS_PER_LITER rather than SCREEN_SCALE_FACTOR and VOLUME_RESOLUTION, avoids all kinds of mistakes like inverting ratios etc.
Have none of you ever refactored code..? Or all of your writing C?
Your library should just expose an interface/protocol signature. Program to interfaces. Anything that is passed in should just implement the protocol. The sleep function should not be aware or care about the input type AT ALL. All it requires is the input to implement an "as-milliseconds()" function that returns a millisecond value. You can then change any internal representation as your requirements change
time.Sleep(1 * time.Second) delaySecs := 1 * time.Second
time.Sleep(delaySecs * time.Second)
Now I insist on using the durationcheck lint to guard against this (https://github.com/charithe/durationcheck). It found a flaw in some exponential-backoff code I had refactored but couldn’t easily fully test that looked right but was wrong, and now I don’t think Go’s approach is reasonable anymore.int64 * Duration → Duration and Duration * int64 → Duration both make sense, but I gather this only works with constants. For other values, I believe Go only gives you Duration * Duration → Duration which is just wrong, wrong, wrong, requiring that one of the two “durations” actually be treated as though unitless, despite being declared as nanoseconds.
In the end, it’s probably still worth it, but it’s a case of Go trying to design in a certain way for ergonomics despite lacking the type system required to do it properly. I have found this to be a very common theme in Go. Also that it’s often still worth it, for they’ve generally chosen their compromises quite well. But I personally don’t like Go very much.
https://pkg.go.dev/time#hdr-Monotonic_Clocks
> RRDNS is written in Go and uses Go’s time.Now() function to get the time. Unfortunately, this function does not guarantee monotonicity. Go currently doesn’t offer a monotonic time source (see issue 12914 for discussion).
https://blog.cloudflare.com/how-and-why-the-leap-second-affe...
> time: use monotonic clock to measure elapsed time
There is a Duration::new() constructor that's more ambiguous, but it's your choice as a dev to be ambiguous in this instance, and code review should probably catch that.
sleep(5.seconds)
sleep(1.minute)
sleep(2.hours)
etc etc sleep 3.seconds
Hard to to be more concise than that Future.delayed(const Duration(seconds: 2)
Future.delayed(const Duration(milliseconds: 2000) def sleep(num : Time::Span)
# do something here
end
I would call it like: sleep 300.seconds # Session inactivity, in seconds
time.sleep(300)
# Prevent pileup of unprocessable requests, in seconds
request_timeout = 10 # Sleeps before continuing
time.sleep(…)
# Time before request timeout
timeout = …time.sleep(300)
Put your units in the method name:
time.sleep_seconds(300)
Likewise, if you're making a .length() method where the units are at all ambiguous (like the length of a string), name your units. Bad: str.len(). Good: str.len_chars() / str.len_utf8_bytes(). (I'm looking at you, Rust!)
poll_file_descriptors(read_descriptors, write_desciptors, timeout_in_seconds)
it would be awkward to change the name of the function just because on of the arguments happens to refer to time. typedef int seconds;
and time.sleep(seconds duration);The operator's requested speed was input in Feet Per Minute. The output to a Variable Frequency Drive was in tenths of Hertz. The tachometer feedback was in RPM, and to top it off all the internal calculations were done in Radians-Per-Second.
The first thing I did to get the project back on track was to adopt a standardized variable naming convention that included the units. For example the Operator Request became operator_request_fpm_u16. You then knew immediately you were dealing with Feet Per Minutes, and that it was a 16 bit unsigned variable.
After the variable name cleanup many of the bugs became self documented, when you saw something like "operator_request_fpm_u16 / vfd_hz_s32" in the code, you knew there was a problem that needed to be fixed...
My take is that Hungarian notation exists to work around a deficiency in tooling. I think this is clearer when encoding e.g. u16 into the identifier, since that information is redundant (the declaration or schema already encodes that it is u16).
> Frink is a practical calculating tool and programming language designed to make physical calculations simple, to help ensure that answers come out right, and to make a tool that's really useful in the real world. It tracks units of measure (feet, meters, kilograms, watts, etc.) through all calculations..
I use Frink very regularly as a calculator. I've got a keybinding in emacs to bring it up in comint-mode, which works very well.
It's also a general-purpose programming language, at least in theory. I actually tried using it for that purpose once, with I think a few thousand lines of Frink code in total. It was not a pleasant experience. It's fine if you want to write a short script that's dimensionally aware, but for modelling of a complex physical system there are much better tools, such as Modelica.
Then, somebody asks me to code in the temperature of a system. And I have to think: "Is now really the time that I want to teach people the difference a kelvins and celsius?"
So my rule becomes, SI "except" temperature. Sigh...
Haven't come across temperature however we would probably stick with kelvin.
We use a strict set of units in databases and while processing, conversions are localized if necessary only at the view layer.
We also only use UTC for date/times.
We only use E164 format (without spaces etc) for phone numbers: e.g. +12345678901 for an example number in OH, US. see National format https://libphonenumber.appspot.com/phonenumberparser?number=...
We only use iso3166-1 country codes and iso3166-2 region codes and translate on view.
If you do, be explicit about it either in parameter or function name. I'm not going to put you on my shitlist if you're naming your function `microsleep`, but if I have to go look into implementation to see that you count timeout on your database in microseconds (looking at you, couchbase, like you ever could return something from a larger dataset in microseconds, lol) or, even worse, cache expiry time in minutes (hello unknown developer), I am going to go on the internet and complain about you.
From a hardware perspective it makes sense to use K because you can encode the image directly using unsigned numbers plus some gain to allow for fractional measurements.
- avoiding naming things if you can help it
- using tooling to autogenerate names
- avoiding too short, likely overloaded names like `id`, `name`, and `url`
- don't choose lazy pluralization - eg instead of `names/name`, use `nameList/nameItem`
- encoding types to make Wrong Code Look Wrong, a famous Spolsky opinion (lite hungarian notation)
- coming up with grammars for naming, eg React had an exercise to name its lifecycles combinations of THING-VERB-ACTION, like `componentDidMount`, before open sourcing, which helped learnability
pulled from my collection of Naming Opinions here: https://www.swyx.io/how-to-name-things
Isn't this just extra noise? If the type of the variable is an Array wouldn't `nameArray` be superfluous? Worse still is if the type changes but the name stays the same.
I get the advice is probably Javascript specific, but even in a Typescript world it doesn't make much sense to me to do this kind of type-in-name encoding.
In a small enough context, no way :)
> don't choose lazy pluralization - eg instead of `names/name`, use `nameList/nameItem`
This just seems redundant and is a personal annoyance. Is plural meaning list or sequence not widely understood enough?
That said, "percent" is a common and precise word, and I haven't come up with anything quite as good for variables in range [0,1]. I generally use "ratio," but I don't love the word. Is there anything better?
Unfortunately "Theory of Mind" is weak in people on the spectrum.
Documentation should always be secondary to an obvious, descriptive interface. We've evolved beyond register positions and phonebook-style paper documentation. Use the tools available to you.
void addPadding(int height) {
}
Types are useful, and even as a Python programmer I gravitate towards type hints for all new code, but any application where a programmatic type maps to a real-world type with common conversions (like, say, pixels, em, en, millimetres, centimetres, or inches in the above example) tends to be a victim of the same issue, where the type system isn't expressive enough to clearly describe the real-world type being assumed by the function.setSpeedInKilometersPerSecond vs. setSpeed
I'd rather have it baked into the language ala CSS ("100ms").
But more important: there's middleground. What about setSpeedMmps() if setSpeedInMillimeterPerSecond is too noisy.
My ROT is that the unit or type designation schould not overshout the most important info. Which, in case of setSpeedInMillimeterPerSecond is arguably, indeed the case. But the solution is almost never binary, but a more succint version. Everyone understands km, mm, sec, hr (though the win with such is silly) px and so on.
https://www.boost.org/doc/libs/1_78_0/doc/html/boost_units.h...
FWIW, in "TimeSpan for the .NET crowd", you would write "00:00:50" for (zero hours, zero minutes and) fifty seconds. Hours, minutes and seconds cases are easy now that we know the format.
Yes, you can omit some of the zeros, but your comment above makes the case that for clarity, you should not.
Most currencies do not have "Cent" as a base unit. And there are currencies without a smaller base unit, most notably japanese Yen.
The problem is, that there exists no established currency neutral name to distinguish between the two possible units that coincide in such cases as Yen.
I was thinking about the following:
class AmountOfMoney
property int FinerAmount
property decimal CoarserAmount
In the case of Yen (FinerAmount == CoarserAmount) is always true.Any suggestions for better wordings?
def my_sleep(*, seconds: int):
pass
my_sleep(seconds=3)
This will force those who use your function to use the parameter name. A sort of documentation I suppose. std::thread::sleep(Duration::from_millis(300)); delay 0.3;
Or with package Ada.Real_Time: delay To_Duration (Milliseconds (300));
Or use `delay until`: delay until Clock + Milliseconds (300);
`delay until` is useful in loops because `delay` sleeps at least the given duration, so you get drift. (You call Clock only once and store it in a variable before the loop and then update it adding the time span each iteration) std::this_thread::sleep_until(std::chrono::system_clock::now() + 300ms)
That's true for all functions that take timeouts (At least since C++11).edit: s/thread/this_thread/
* https://docs.oracle.com/javase/8/docs/api/java/time/Duration... * https://docs.microsoft.com/en-us/dotnet/api/system.timespan?... * https://en.cppreference.com/w/cpp/chrono/duration
If you're putting something in a config file, as the blog says - always put the unit in the key name.
You can set ones to ignore, a minimum value for the rule to apply, or require the variable to be a const.
For instance 7, 24, 60, 1000 allowed in defining units of time, and any other intervals are defined as 5 minutes = 5 x 60 x 1000.
Still, I've had plenty of experiences with 'is this unit in seconds or milliseconds'. As I recall, Gilad Bracha at one point looked at attaching units to numbers but I think he got wrapped around the axle on automatic conversions - if you divide meters by seconds, this is a meters/second unit. But do you convert to newtons or joules automatically too?
int length = 5
Is quite different than:
int lenght_in_meters = 5
http://catb.org/jargon/html/M/magic-number.html
https://en.wikipedia.org/wiki/Magic_number_(programming)
delay_ms = 300;
[...]
Thread.sleep(delay_ms);
Works in any programming language. delay_seconds = 300;
[...]
Thread.sleep(delay_seconds);
This kind of bug can easily surface if you need the constant for calls taking different units and can be hard to spot. using namespace std::chrono_literals;
auto lesson = 45min;
auto day = 24h;
std::cout << "one lesson is " << lesson.count() << " minutes\n"
<< "one day is " << day.count() << " hours\n";
and you can compare durations specified with different units etc. There mpusz/units library, which may go into the standard at some point, lets you do this: using namespace units::isq::si::references;
// simple numeric operations
static_assert(10 * km / 2 == 5 * km);
// unit conversions
static_assert(1 * h == 3600 * s);
static_assert(1 * km + 1 * m == 1001 * m);
and also write conceptified functions which "do the right thing", e.g.: constexpr Speed auto avg_speed(Length auto d, Time auto t)
{
return d / t;
}
this will normalize appropriately. But - the specific units used will need to be known at compile-time. Choosing units at run-time is a different kettle of fish.Some people complain that it involves too much typing or takes up too much space. In reality, we spend much more time reading code than we do writing it, so names that provide context, direction, and fore-shadowing are really useful.
I still tend to write method names and important variable names like that to this day in pretty much any language I use. IMHO, its a great hack.
An extreme example is in the NSBitmapImageRep class [1]
[0]: https://www.cocoawithlove.com/2009/06/method-names-in-object...
[1]: https://developer.apple.com/documentation/appkit/nsbitmapima...
> request_timeout = 10
> Accept one of these instead:
> request_timeout = 10s
> request_timeout_seconds = 10
What does ”s” imply? Can I write “10m” for 10 minutes, or is that 10 months? Non-standardized syntax is dangerous.
For config files, I use RFC 3339 duration syntax; i.e. “PT5M” for five minutes. It was the most standardized syntax for semi-human readable time periods which I could find.
`s` is actually one of the SI base units: https://en.wikipedia.org/wiki/International_System_of_Units#...
edit: actually, month should never be used for the case described anyway... 28-31 days?
On JSON, I always do { unit:XXXX, value:XXXX }, it is quite verbose but it does help a lot when you least expect it.
I also wrote a small utility that transforms between values like { unit:'m', value:5 } to { unit:'km', value:0.005 } so that makes it super easy for me to pipe stuff from one context to another when is needed.
The java.time API goes in the same direction, but is obviously limited to things around time.
I think units should be really expressed with the type system, because types give meaning _and_ safety. If used APIs require typed units you wouldn't even need a coding convention to put the unit in a variable name.
Types like e.g. Duration (for e.g. sleep), Instance (for calculating time passed based on monotonic timestamps), Distance etc.
Through at the same time in static typed languages being generic over the unit is often unnecessarily and hinders productivity (e.g. Duration<TimeUnit> with types Duration<Seconds>, Duration<Hours>) similar having unit types (e.g. passing instances of Seconds, Hours to sleep) is also often not necessary and more harmful then good. (Exceptions exists, where you e.g. need to have where high precision and range while at the same time need to keep storage constraints as small as possible while also juggling units, like some scientific or embedded applications).
It also doesn't make a unit error stick out, as you have to take the time to specifically hover to check. What I mean by this is consider you've opened a mature code base and are browsing around, and scroll past:
timeout = 60000;
I'd assume this is milliseconds, and whether I check would depend on what I'm doing and probably also my mood.Whereas if I see:
timeoutSeconds = 60000;
It'll draw my attention and no matter what I'm doing I'll either open a bug or just fix it.Without this small info, you have to grep the docs or worse, grep the source code itself. There's nothing wrong with looking at the source code but you shouldn't have to if you just want to use the public interface.
Pass around types encoding certain quantities. You do not want a Thread to "sleep for x units", but rather "sleep for this specific Duration", therefore pass around Durations and implement appropriate utilities `.toSeconds(Duration d)`, `.fromTime(Time t1, Time t2)`. The very moment your `.frobnicate(int time_in_seconds)` gets wrapped in `foobar(int duration)` all meaning is lost and you have no control over it.
Type systems are there to encode meaning (sometimes including possible values) behind a value - use it. And if you use highly dynamic prototyping language in production... Well, inability to encode and enforce meaning behind a value is part of the compromise.
> Option 2: use strong types, An alternative to putting the unit in the name, is to use stronger types than integers or floats. For example, we might use a duration type
When the input box is a function the only unit you need to know is 'distance'. So I don't completely agree with the article. A sleep function does not need the unit in it's name but should accept a domain unit: duration. The function itself can convert it to something the function can handle.
`sleep(2s+5ms)` should just sleep 2 seconds and 5 milliseconds.
[0] https://github.com/mpusz/units
[1] "Daniel Withopf - Physical Units for Matrices. How hard can it be? - Meeting C++ 2021" https://m.youtube.com/watch?v=4LmMwhM8ODI
Contrarian opinion: You don't. You make people look up and internalise such information; that way they'll actually learn, as otherwise they'll forever stay within the realm of "beginner". Perhaps that's a goal for those who want to make programmers fungible (and I suspect a lot of the "readability" movement is merely an extension of that), but I don't think that's something we should encourage.
What you say works at a small startup. Absolutely.
My reality and lots of other people at companies with larger code bases: you need to constantly look at code you have never seen before in one of many languages used at your company using one framework or another that is out of date by years or just came around the corner and you just heard the name of for the first time.
This is the reality of many an architect or team lead or principal engineer. Of course you will say: why make everything better for them, works fine for me who I only ever work in language X with framework Y? I agree that makes sense for the you of now. Why care?
Think about the future you. The principal engineer you. The architect you. Heck even just the 9 months from now you when you have moved on to the next framework. Never mind any other changes.
I would rather have my team spend time internalizing good design rather than the trivia of what units are associated with every numeric argument in every codebase.
If time.sleep took a timedelta it becomes impossible to use incorrectly with a type checker since the caller specifies their own units. There is no merit to worshiping ambiguous design.
Task.sleep(nanoseconds: 3e11) Date().advanced(by: .hours(2) + .seconds(10))
I'm honestly not sure why TimeInterval doesn't include this representation by default.1. https://developer.apple.com/documentation/dispatch/dispatcht...
julia> using Dates
julia> Millisecond(10)
10 milliseconds
julia> sleep(Microsecond(100))
julia> sleep(Millisecond(10)) def frobnicate(timeout: timedelta) -> None:
...
timeout = timedelta(seconds=300)
frobnicate(timeout)
Now instead of remembering that frobnicate takes an argument of seconds, one now needs to remember it takes a completely different type, the constructor for which now also needs to be memorized. When the main problem presented is one of memorization, this seems obviously worse?Making implicit units explicit is a great example of that. Look at Martin Fowler's writings on Money types, for example. Or look at the $125 million failure of a space probe because people used implicit units: https://www.simscale.com/blog/2017/12/nasa-mars-climate-orbi...
This seems less like a memorisation problem and more of an IDE one. If I'm presented with a type in my IDE, I can just look through its constructors and methods to find the one I require; no need for memorisation.
1. https://www.php.net/manual/en/function.sleep.php
2. https://www.php.net/manual/en/function.usleep.php
3. https://www.php.net/manual/en/function.time-nanosleep.php
An example of use is in the FHIR (Fast Healthcare Interoperability Resources (hl7.org/fhir)) standard as a valueset.
https://www.hl7.org/fhir/valueset-ucum-units.html
[edit to remove dupe linke]
Would love HN’s recommendations for blogs/writing on how to do better programming.
A lot of writing that I am exposed to is about the business of software, or engineering leadership, or architecture/tech stacks — which, to be clear, I really like reading, and find value in!
But I would love to read more thoughts on how to actually write good code on a regular basis.
Also, I love F#'s units of measure.
https://overcoddicted.com/domain-specific-types-for-primitiv...
@lru_cache(maxsize=None) # memoize
def seconds(time_code):
"""number of seconds defined in DNS format: '2m30s' = 2 min 30 sec = 150s. h = hour, d = day, w = week"""
result = 0
number = ""
for char in time_code:
if char.isdigit():
number += char
else:
secs = {"s": 1, "m": 60, "h": 3600, "d": 24 * 3600, "w": 7 * 24 * 3600}[char]
result += int(number) * secs
number = ""
return result void foo(Duration duration);
var result = foo(Duration.ofHours(2));
You can also do simple calculations easily var bar = Duration.ofHours(2).plusMinutes(30);It's funny how the article talks about using the type system, but the title does not.
Instead always use "TimeSpan dataCacheTime" as this is more flexible: can hold values from sub-millisecond up to multiple days, and is easier to use with framework methods that will also expect this type.
In other words, use the appropriate type instead of putting the duration unit name in the var.
Lemme tell you, every single organization that respect itself, it will tell you, during code review, that you do not use magic numbers in code. It's a red flag, so his "Do this: frobnicate(timeout_seconds=300)" will actually fail the code review.
Here is what you would do instead. Declare a constant in some constants only unit, with explanation what it does and then use that constant, like this:
//constant unit
const TIMEOUT_SECONDS = 300;//bla bla why this is 300 seconds, usually from client requirement
//code unit
frobnicate(TIMEOUT_SECONDS);
const TIMEOUT_SECONDS = ONE * HUNDREDS;
The definition of ONE and HUNDREDS is left as an exercise.Snark aside, the rule zero of all coding conventions is that they should not be applied if they do not make sense in a specific case.
frobnicate(300) // Duration in seconds to do X
Sure, change the variable names or whatever as well, but ... kinda seems we already have a way to make less obvious things obvious?
If it’s in the parameter name it’s impossible to use it without doing it correct, which is almost always preferable.
But one better than documenting comments is no documenting comments. Selfdocumenting code is certainly not always feasible. But in this case it's both possible and easy.
Process.sleep(_milliseconds = 300)
I started using it more and more because it makes the code more readable.https://www.joelonsoftware.com/2005/05/11/making-wrong-code-...
It also makes the distinction between "apps hungarian" (good, unit-types, eg: meters, seconds) and "system hungarian" (bad, storage-types: float, int)
Sadly I've never worked on a codebase that follows this.
This did the job really well, but it was incomplete. Being able to create new types dynamically, when multiplying/dividing was something that was always missing. I guess now you could probably do this with macro programming, but back then I don’t think my language had it.
https://geant4.web.cern.ch/sites/default/files/geant4/collab...
The SI unit is seconds. So unless you have some weird aversion to using decimals to represent smaller units, any kind of delay function should take input in seconds.
Not sure if this should be a cautionary tale about using units so much as using the correct units in the first place.
Apparently there's a Java function that lets you specify time units as secondary input. Use that to make in unambiguous.
The foundation of web, javascript is one of them. Send seconds in api to a frond-end without documenting is just confusing and hugely ambiguous.
And it makes more sense from the GUI's perspective. What did you mean paint next frame after 0.016s? Isn't it much easier to read when write it as 16ms?
pause for 3 seconds
With some other options for units. https://github.com/rzimmerman/kal#asynchronous-pauseIt seems types are useful piece of information. Who would expect?
This is fine but relying on an IDE for code reviews really sucks. I'd much prefer to be able to do it on github. And it sucks for any language which infers types :( Scala is in particular awful for reviewing on github
I wish code review tool could provide this hover functionality too.
sleep 5.seconds
or temperature = 0.17.kelvin
Not sure that last abuse actually works, but one may try. :-PSame thing with explicit memory allocation. If for whatever weird reason I need to explicitly allocate RAM I first write deallocation.
For example, when I create a static value to hold a constant, I usually do it like so:
static private let _maximumButtonHeightInDisplayUnits = CGFloat(30)Depending on your language, and how much type safety you want, you could use something Haskell's units library [3].
I believe F# also has units [4], possibly built in to the language? (I've never used F#.)
[1] https://en.wikipedia.org/wiki/ISO_8601#Durations
[2] https://docs.oracle.com/en/java/javase/11/docs/api/java.base...
[3] https://hackage.haskell.org/package/units
[4] https://docs.microsoft.com/en-us/dotnet/fsharp/language-refe...
Take heed young adventurer: This balm will not solve all your ills. It will be your hard work, compassion, and perseverance that will bring the new golden age and sustain it.
TLDR: We've done this before, it solves some problems and causes other.
time.Sleep(42 * time.Millisecond)
time.Sleep(42 * time.Second)
time.Sleep(42 * time.Nanosecond)
time.Sleep(42 * time.Hour)The fact you can go from int to duration without specifying the unit does not give you the strong benefits. It's better than nothing though
time.Sleep(time.Second * time.Second) def foo(x):
print(x)
accept `foo(x='I accept keyword arguments!')`Somewhat relevant.
Thread.sleep(millis: 300)It was a pretty safe guess, though, the number of bugs related to this is pretty epic.
Thread.Sleep(TimeSpan.FromSeconds(3));
or with its int overloaded method: Thread.Sleep(millisecondsTimeout: 3000);let time: float<s> = 0.1<s>
let length: float<m> = 10.0<m>
let speed: float<m/s> = length / time
```
time.sleep(timedelta(minutes=5).total_seconds)
```
Or
```
time_to_sleep = timedelta(minutes=5) time.sleep(time_to_sleep.total_seconds())
```
For example...
time.sleep(timedelta(minutes=5).total_milliseconds)
...looks just as plausible. So what does this solve?Why would you want to enter a percentage of a minute?
[0] https://en.wikipedia.org/wiki/International_System_of_Units
When I hover over time.sleep(delay) in my Python IDE the detailed pop up shows me that this sleeps for delay seconds and that delay is an int.
When I hover over time.Sleep(wait) in my Go IDE, the popup shows me that time.Sleep takes a time.Duration.
Please get better tools and stay the heck away from both my languages and coding style guidelines.
"why isn't my code doing anything?!"
This should've been Option One
I'd argue that it's fine if the accounting app is never going to handle multiple currencies.
Length object = Length.Parse(string);
or
Temperature object = Temperature.Parse(string);
this would but much simpler!
await sleep(5 * MINUTES)
Where MINUTE and MINUTES is a constant for 60 * SECOND.
...
time.sleep(three_seconds)
When it's already too hard knowing the units of the function you're supposed to know, then why does he think he's supposed to code in the first place?
Bat-shit ridiculous. Of course people with low skills will be all over this, happily embracing it, but it's still absolutely ridiculous.
If you can't even remember this, then you should not be programming in the first place!
Do you label your variables a, b, c,...?