4 ms·
Seriously, since everything I work in is denoted as "cents" from backend to frontend, I personally had never understood the need This matches my exp
by JohnBooty 4y ago
Seriously, since everything I work in is
denoted as "cents" from backend to frontend,
I personally had never understood the need
This matches my experience.
Obviously actual strict typing has its benefits, but as far as developer ergonomics are concerned, IME you can get about 95% of the benefits just by following a convention of including unit names in identifier names.
It's easy to see why this Ruby code might fail:
def launch_rocket(distance)
# what units are we expecting?
end
launch_rocket(42)
But this is virtually impossible to screw up, and reduces developer cognitive load:
def launch_rocket(distance_km:)
# blahblahblah
end
# not happening unless developer consumes a large number
# of drugs
distance_miles = 42
launch_rocket(distance_km: distance_miles)
- Swizec 4y agoEasy! # not happening unless developer consumes a large number # of drugs distance_miles = 42 launch_rocket(distance_km: distance_miles*1.6) Aaaand we're off by 0.3924km. Enough to fit 4 football fields with a few meters left over. Oopsies Units are hard. Never under-estimate the ability of programmers to think they're converting but get it slightly wrong.
- deleted 4y ago[deleted]
- sgtnoodle 4y agoIt's within a significant digit of the specified distance, though. A few football fields of error seems pretty good to me for 42 miles. What's the required accuracy and precision for this rocket launching system?
- Swizec 4y agoWell if it’s a boom rocket for war, the expected precision these days is the size of a car if not smaller. I think even dumb gravity bombs in ww2 were more precise than “a few football fields”
- Symbiote 4y agoRussia has been criticised and ridiculed for bombing civilian targets when they are presumed to be aiming for military targets a block away.
- JohnBooty 4y agoI qualified my answer with a "95%" because yeah, I think it's good enough about that often. If we're launching physical rockets then yeah, I would agree that that is probably not a job for dynamic typing.
- fr0sty 4y ago> [follow] a convention of including unit names in identifier names. appending units to identifiers helps, but it relies on a developer's eyeballs to spot any errors. It would be infinitely preferable if the type system would simply enforce this for you and developers not have to expend cycles reasoning about this stuff themselves.
- bornfreddy 4y agoIf you stick to just a few units throughout the system (ideally, use a consistent base unit everywhere and convert any external measurements to it), you can avoid the rest of the problems. Static typing is great, of course, I just don't agree that this can't be solved to almost the same level with languages with dynamic typing.
- epgui 4y agoThe keyword is “almost”, and whether you need stricter typing depends how much you care about software correctness (or to be pedantic, the class of problems that the typing system can fix for you in the language in question).
- JohnBooty 4y agobut it relies on a developer's eyeballs to spot any errors. I'm assuming a relatively normal/sane environment where code is reviewed at pull request time, and there is a test suite. developers not have to expend cycles reasoning about this stuff themselves It is not my experience that `launch_rocket(distance_km:)` requires any extra cycles whatsoever. I mean, as a coder, I'm going to have to be cognizant of the type anyway, even if we're doing `launch_rocket(distance:)` in a strongly typed language where `distance` is something of type `RocketLaunchDistance` or whatever. I'm not arguing against static/strong typing in general or anything. Definitely lots of times when it is the clearly superior choice.
- svnpenn 4y agohere it is with Go: package main type km int func (distance km) launch_rocket() {} func main() { distance_miles := 42 // type int has no field or method launch_rocket // distance_miles.launch_rocket() // this works, but is obviously wrong: km(distance_miles).launch_rocket() }
- apgwoz 4y agoGo does pretty good with time. You could replicate that for distance, too.
- generationP 4y agoNope, this will lead to the same problem that the OP has discovered, viz., that a dependency update creates silent errors. Unless the argument is a keyword, but everyone is too lazy for that.
- soulofmischief 4y agoYeah, to me the moral of the story was write more tests.
- zaphar 4y agoIf you encode your test in a type then you don't have to write it separately. Even in python you can use types for this sort of thing it will blow up at runtime but so will your test. Just have a US Money type that won't return a number unless you call as_cents() or as_dollars(). Now much harder to misuse.
- JohnBooty 4y agoI mean, if your coders are too lazy to give identifiers meaningful names, you probably have bigger issues. It takes very close to zero effort.
- tom_ 4y agoThe case you claim requires drugs will happen eventually even if everybody is sober. I've seen it many times, and I doubt I'm the only one. Somebody is tired, or they get the computer to perform some bulk change and get it wrong, or they are doing some rote transformation and they miss a case. If you want to do a bit better, a simple way of handling it is to provide a separate type that handles the abstract quantity (distance, money, etc. - you'd probably want to be cleverer about money if you deal with multiple currencies though), and ensure that values of that type can be converted to and from numbers only when the units are explicitly specified. So then you end up with functions that consume a distance looking something like this: void launch_rocket(Distance distance) { call_ancient_fortran_routine(get_miles_from_distance(distance)); } And functions that create distances looking something like this: launch_rocket(create_distance_from_km(100)); What you now can't do is create a value that's of one unit, and pass it to something that expects another unit - the issue doesn't really arise, as Distance values themselves don't have specific units. They are a black box that somehow encodes a distance, and you specify the units used explicitly when initializing and you specify the units desired explicitly if retriving an actual number. (Turning a distance into a number would ideally be a rare case, though sometimes you'd need it. You'd provide maths functions for all operations required, so you'd hopefully rarely need the number for calculation purposes. For displaying in any UI, there'd be a function to convert it to a string that respects the user's locale and distance unit preferences. And so on.)
- JohnBooty 4y agoWhat you're suggesting is obviously more foolproof for sure. However it's definitely my observation that there are lots of times when we're passing numbers around and we don't need something quite as robust. I'm not sure that replacing every number in an application with a custom type is typically the best use of time, and certainly there are performance reasons why we might sometimes want to pass fundamental numbers around instead of complex types. In those cases, adding units/types to identifiers offers an awful lot of value for close to zero effort. The case you claim requires drugs will happen eventually even if everybody is sober. I've seen it many times, and I doubt I'm the only one. I've never seen it, but that's probably because in my 25 years of writing code I have rarely seen the convention followed in the first place because coders tend to be aggressively disinterested in writing maintainable code. But seriously, if `launch_rocket(distance_km: distance_miles)` eludes the original coder and the code reviewers and future coders working with that code and it eludes your test suite... damn. You've got big problems.
- andreareina 4y agoI've seen it happen.