Live data from Hacker News

Twenty five thousand dollars of funny money

rachelbythebay.com

151–160 of 175 posts

Re: Twenty five thousand dollars of funny money

#151

Earlier quoted context omitted.

I fend off the "magic numbers" people with a variable with a name that describes what the magic number is for. var expectedAccountBalance = 2500 expect(account1.balance).to eq expectedAccountBalance expect(account1.balance).to eq account2.balance

That's a good enough (initial) workaround if you're in an environment where you're in the minority (or alone) w/ your opinion and you still want to do the right thing personally. I would advise you to try and convince your peers though and teach them the better way because I suspect that the people that you're fending off would not do what you did but rather just go w/ the one expect(account1.balance).to eq account2.…

as long as there is

expect(account1.balance) eq 2500 before that isn't really a big problem.

Also "how to do it" is kinda different topic to "how to convince peers that's the right way"

Re: Twenty five thousand dollars of funny money

#152
This discussion reminds me that F# has units of measure support in the language. For example constants can be annotated with units[1]:

    1.0
    55.0
Tangentially, it also reminds me that some popular languages don't have a binary coded decimal type for example go and java(?). C# and standard sql support decimals, so your $1.02 doesn't get approximated as $1.01999999

Go has at least 25 community decimal packages[2], but who knows which is a good one to use.

[1] https://learn.microsoft.com/en-us/dotnet/fsharp/language-ref...

[2] https://pkg.go.dev/search?q=decimal&m=

Re: Twenty five thousand dollars of funny money

#153

Earlier quoted context omitted.

In the mid-2010s I worked at a company that switched from using raw ints to refer to database rows to using (in C# terms) "Id ". Across the entire codebase, we discovered an entire class of bugs that only never cause any issues because all the important rows in all the important tables had Id 1 (e.g. Currency 1 was USD and Country 1 was USA) - so in a few places where the ints got mixed up, the correct row was still…

Something I learned long ago, but occasionally disregard to my peril: if you see something that looks like a bug, but the code/system still works, stop and figure out why it works. It's very easy to mentally shrug and move on, but more often than not it comes back to bite you; maybe it's a code path that's rarely triggered, e.g.

I have the same policy for similar circumstances. Sometimes, something isn't working, and I make a change that fixes it, but I didn't expect that change to fix it. Almost always it's worth me investigating why it's now working, because it indicates a deeper problem that would come back to bite me.

Re: Twenty five thousand dollars of funny money

#154
post #143
post #122

Earlier quoted context omitted.

Using types that encode the units, as suggested in OP, is strictly better.

You're both right, but not all languages give you the choice. And a lot of types are implemented badly. For example as a subclass of a number class, so that `amountDollars + timeSeconds` and other nonsensical statements aren't errors.

I'm not optimistic on unit types myself. I've found that unit-named variables often with transparent type aliases as documentation (type Sample = usize, type Amplitude = i16/f32) have many of the advantages of distinct units, without their downsides compared to bare numbers. In several projects where I've used naming and transparent aliases comprehensively, I don't recall ever letting a unit mistake escape from my local tree into master, since I reread my own code when committing and merging. In one case (https://gitlab.com/exotracker/exotracker-cpp/-/blob/dev/src/... used to have two EXPLICIT_TYPEDEF) I did add distinct units because I found I was mixing together two types too often during development. Though I find that unit types come with significant disadvantages (ergonomic and semantic flaws), making them far from strictly better than bare numbers (much like Rust is far from strictly better than C/C++/Zig):

- You need an implicit conversion to eg. size_t, otherwise you can't pass (smp: Sample) into array indexing like (amplitudes[smp]) or data slicing, without an extra conversion or accessing the underlying value like (smp.v). But you can't allow (smp += midi_pitch) to convert both arguments to int, then cast the result to Sample when assigning.

- You need some conversion to allow (smp + 1) with type either integer (convertible to Sample) or Sample, unless you want to annotate all arithmetic with boilerplate like (smp + (Sample)1), or (smp.v + 1). I've experienced this problem in my own code, and had to write (smp.v) when my compiler saw (smp + 1) and told me it didn't know whether to wrap 1 or unwrap smp.

- Expressions of type Amplitude * 2 should have type Amplitude. Go's time library gets this wrong, where multiplying Duration * Duration = Duration, which makes sense if Duration is an integer like i32 or i64, but not if Duration is a unit system dimension.

- (not a regression but a limitation) Units won't stop you from adding two temperatures in Celsius. To fix this you need separate coordinate and displacement types, which is a new pile of complexity.

- You may want distinct types for "samples/sec" and "cycles/sec". Modeling this in type systems has multiple current approaches, all of which rely on language support (F#) or complex type machinery I've had issues with.

- You can't easily convert between slices of f32 (like an audio buffer provided by the OS), and slices of Amplitude. Or worse yet vectors of f32 and Amplitude. (This problem affects bulk data in collections, more than scalar types generally passed and returned in the stack.)

Re: Twenty five thousand dollars of funny money

#155
post #122

Just to say it, for measurements that take a unit I am hardcore that you should include the unit in the variable time: `timeSeconds` `distanceMeters` `amountDollars` Have unit tests and everything, but also write your code so that when someone reads it they know as precisely as possible what's going on without other documentation.

Using types that encode the units, as suggested in OP, is strictly better.

If you're the type of moron who will write:

  chargeCustomer(amountDollars: valueInCents)
I have little faith that types would help you. You could simply do:

  chargeCustomer(amount: Dollars.from_int(valueInCents))
And create the same bug.

Positional arguments is just as big an evil as non-typed units, IMO.

Re: Twenty five thousand dollars of funny money

#156
post #44

Earlier quoted context omitted.

Easy! # 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.

It'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?

Russia has been criticised and ridiculed for bombing civilian targets when they are presumed to be aiming for military targets a block away.

Re: Twenty five thousand dollars of funny money

#157
What amazes me is the software running the banking accounts. Whenever I go to an ATM machine, and I get the receipt, I never for a moment think they can be wrong by even one cent. Because they've never been wrong. From time to time you hear of a case where someone found a million dollars in their account, but this gets to be reported in newspaper. And to be honest I don't even remember how many years ago I read such a story.

I imagine there are tens of millions of people accessing their bank accounts every single day. A bank like Chase or Bank or America, or Wells Fargo probably processes millions of transactions per second. With essentially zero errors. Meaning 99.99999999% correctness, with too many nines to count.

How is that possible?

Not to mention, these guy are probably under hundreds of hacker attacks per day.

Re: Twenty five thousand dollars of funny money

#158

I'm gonna be honest, I had never understood the "strict type" argument until right now. Seriously, since everything I work in is denoted as "cents" from backend to frontend, I personally had never understood the need so I'm part of today's lucky 10,000th.

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…

I've seen it happen.

Re: Twenty five thousand dollars of funny money

#159
post #54

Earlier quoted context omitted.

Yeah just means you have to rely on linting and your ide to catch those errors. And hopefully the rest of your team does the same thing. Tho I suppose if it is really important you can put an assert there but I'm not familiar with that wrt typescript, maybe the transpiler would kill that? I've done the occasional type checking in that way in similar languages, it is kind of self documenting too. 90% of the time duck…

If robot0 name is AAA and uid Is BBB, and robot 1 name is BBB and uid AAA, and you call the function checkRobot('AAA'), what sort of assert exactly could differentiate between a name and a uid?

I don't think it is possible in Typescript, but in other languages you can do assertations about custom types. Like

assert istype(whatever, MyCustomType)

Which would throw an exception if whatever is not a "MyCustomType" at runtime.

Re: Twenty five thousand dollars of funny money

#160
post #77

Earlier quoted context omitted.

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…

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. 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…

What 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.

Post reply on HN