4,058 karma · joined September 12, 2018
Making my example more explicit, you'd need syntax along the lines of
// inside crate C
use A::impl std::hash::Hash for i32;
This syntax would explicitly be limited to orphan implementations.I suppose to further clarify, there's still some coherence requirement there in that crate C can't import the conflicting implementations from both A and B. Which could then perhaps be worked around by adding syntax to spell types along the lines of
HashMap<i32 + A::impl Hash, V>
Which you could argue is a form of naming implementations, I suppose? I'm not familiar with ML. You could maybe also think of it as a more ergonomic way of doing (more or less) those wrapper types.In any case, the annotation burden only exists where it's actually needed to enable orphan implementations.
And either way, multiple different impls can safely coexist within the overall set of code that's linked together, with everything being statically checked at compile time.
Let's assume for the sake of argument that the standard library didn't implement Hash for i32.
You could then have two crates, A and B, with different implementations of Hash for i32, and both could instantiate HashMap<i32>.
This can be made to work if we recognize the HashMap<i32> in crate A as a different type than the HashMap<i32> in crate B.
This only really works if orphan implementations are exported and imported explicitly to resolve the conflict that arises from a crate C that depends on A and B.
If C wants to handle HashMap<i32>, it needs to decide whether to import the orphan implementation of Hash for i32 from crate A or B (or to define its own). Depending on the decision, values of type HashMap<i32> can move between these crates or not.
Basically, the "proximity to code location" is made explicit in a way the programmer can control.
This makes type checking more complex, so it's not clear whether the price is worth it, but it does allow orphan implementations without creating coherence problems.
This isn't exactly trivial, but it avoids coherence problems.
So, still a great result, but not as out there as one may think.
I think it's also worth pointing out that in theoretical CS and most of math, it is common to list authors alphabetically. I don't think we have a way of knowing the relative contribution of the two authors. Demaine is obviously accomplished, but I find the kind of hero worship found in this thread distasteful and the facts don't support it here. Give credit to Langerman; Demaine surely would!
[0] Royal you, speaking to a significant fraction of the HN audience.
The combination of all three tends to mostly appear in nation states. They have the motive, and they have the money to fund people with the ability to pull off this kind of attack.
More importantly, everybody can see that debt for investment allows more growth. Just think about how many more people can afford to own their own home thanks to taking on debt. This allows them to pay a mortgage instead of rent, which allows them to build up wealth.
Equivalent effects exist in industry.
Debt is an extremely useful tool. We made the right choice here as a society.
Banter is a matter of wit. You could call it an intellectual pursuit.
Blue collar jobs are primarily not intellectual pursuits. They need their own kind of smarts, but these smarts are relatively orthogonal to the kind of linguistic smarts used in banter, and most importantly the work output itself is not intellectual. There's little chance of the banter directly getting into the work output, and so there's little direct motivation for bosses to police it.
Software development is basically entirely an intellectual pursuit that very much overlaps the wit of banter, and banter is likely to leak into the work output. Hence easter eggs are a thing. So, bosses are more likely to want to police banter-adjacent activities, which has a likely chilling effect on banter itself.
Another, more recent, factor is that more software development activity is online/remote and therefore lower bandwidth. The subtleties of banter don't convey as well as they would in-person.
If you separate the operations like that, the intermediate result is rounded. The final result is then often different from the result of the FMA, which doesn't do the intermediate rounding.
That's using SIMD, but so is Nvidia for all intents and purposes. Those "cuda cores" aren't truly independent: when their execution diverges, masking is used pretty much like you'd do in CPU SIMD.
A lot of the control logic is per-SM or perhaps per-SIMD unit -- there are multiple of those per SM. You could perhaps make a case that it's the individual SIMDs which correspond to CPU cores (that makes the flops line up even more closely). It depends on what the goal of the comparison is.
In Germany, crossing at a red light is very frowned upon. Many Germans even wait at a red pedestrian light in the middle of the night when there's zero traffic.
Crossing streets in places without pedestrian lights or designated crossings is very common, though, and I believe usually legal. (I certainly haven't heard of anybody being fined for it.)
Do you want the best doctors involved in care for patients and training juniors, or do you want them to spend time jockeying for a position in the hierarchy because that's a plausible but also the only way to 2x their income?
This doesn't fully answer the question, of course, but it suggests that large pay disparities are extremely wasteful for society as a whole.
Consider a problem like edit distance, which is solved using an n^2 dynamic program. What are the n separate problems there?
Sure, you're filling out a table with a nested loop, but that's a very mechanistic view. I don't believe that treating the n outer iterations as separate problems gives any real insight.
They're really a kind of database. Perhaps a better way to think about it is in terms of "commons". Consider how creative commons licenses are explicit about requirements like attribution, noncommercial, share-alike, etc.; that feels like a useful model for talking about weights.
(I have driven in Italy as a foreigner several times without ever receiving a ticket.)
Even allowing for some hyperbole, your programming experience is extremely different from mine. Looking anything up outside the IDE, let alone via Google, is by far the exception for me rather than the rule.
I've long suspected that this kind of difference explains a lot of the difference in how Copilot is perceived.
Powers of two are more natural in a binary computer. Then add the fact that 8 is the smallest power of two that allows you to fit the Latin alphabet plus most common symbols as a character encoding.
We're all about building towers of abstractions. It does make sense to aim for designs that are natural for humans when you're closer to the top of the stack. Bytes are fairly low down the stack, so it makes more sense for them to be natural to computers.
They could be using transformers, sure. But plenty of transformers-based models are not LLMs.
If this is going to be an exception, it'd be truly interesting.
However, it is generally too hard (in C and C++) for compilers to tell whether you were wanting to do the thing at any one particular place.
So compilers have two options: Assume that you never do the thing, or always assume that you don't do the thing.
The former is often better for performance in practice, and it's true most of the time, so here we are.
As has been pointed out elsewhere, one of the strengths of Rust is that it shifts how pointers (references) work and allows the compiler to more often know for certain that you don't do the thing, without making assumptions.
{ ...; expr } --> x;
// x is a new variable initialized to expr
I feel like this would help readability when the compound statement is very large.What problem formulation do you have in mind?