4,901 karma · joined September 13, 2020
Additionally, going through the FFI from C++ to Python and back to C++ has a cost. Eliminating this overhead was one of the motivations behind Julia.
So if even a personal site being done with no deadlines, no pressure from management to include analytics etc. can't do it, because it wants to display a bunch of photos, then I don't think we can expect "web-scale" websites to achieve it.
In my experience, most mails don't actually use any styling beyond the automatic blockquote for the endless quote chains that mail clients automatically add. So people are wasting storage and transfer for something they don't really use.
When I see a mail which could actually use some styling, it's usually something involving maths and HTML is not suitable for that case. People usually send LaTeX snippets in plain text in this case.
And while I'm at it, the endless quote chains are another thing which has no reason for existing. Each mail in a thread steadily grows in size, because mail clients "conveniently" always quote everything and put the text cursor above it so that the user doesn't stop for a moment to look at the size of what they're sending and consider the absurdity of the situation, when their client is perfectly capable of reconstructing the whole thread without copying all mails in each message. Quoting should be opt-in on a case-by-case basis. As they function in the wild today, the quotes are just useless garbage attached to everything.
From what I've been told, C++ games do another thing and just use arena allocators, which make allocations cheap (just an integer addition, provided you know a reasonable upper bound on the size of an arena, and if you don't, you may reserve a large piece of virtual memory, and commit it later in reasonably small, but also not too large chunks) and free-s even cheaper (either reset the integer, so that the arena can be reused, or a single syscall to unmap the whole arena in one go). That's a different strategy than making a lot of little allocs and frees, and then trying to minimize them by reusing objects, which is also quite hairy.
That's a good point. I consider myself debunked.
Overall, it gives the impression that this person actively searched for something to feel insecure about and then wrote a looong article about it.
Also, it's weird to label mincing garlic yourself as snobby, when it's the cheap option done by almost everyone. Forget about finding jarred garlic in a pleb fridge. Hipsters with creative eating habits? Much more likely. And for people without applicable disabilities, jarred garlic doesn't save all that much time. Time spent mincing garlic barely registers compared to other activities in the kitchen. And jarred garlic is A LOT more expensive[1]. But if you have disabilities, it may be the rational choice, yup, no argument about that. And even when it's not the rational choice, who cares?
When I cook, I don't check on Twitter if my way of cooking is approved by snobs. Go on detox from Twitter. Your life will get much better than by fighting imagined ableism.
[1]: No surprise here really. Fresh food is almost always several times cheaper than processed food. After all, it's less work to make it.
[1]: <https://www.researchgate.net/publication/2809440_Using_de_Br...>
[2]: <https://www.chessprogramming.org/index.php?title=De_Bruijn_S...>
This expression matches on the effect flip() and handles it with the code on the right hand side of -> which becomes the value of the function invoking the effect. In this case the handler runs the continuation for both possible values (resume true and resume false, i.e. the function was called once, but it will return twice, with true and false substituted in the place of flip() in the place which used this effect) and returns their mean as the average.
I.e. each time calculation calls flip(), the effect handler will continue the function for both true and false and then take the mean of those two results. Comments explain that flip() should simulate a coin flip, which generally gives equal probability (1/2) to true and false. Each flip() in calculation was simulated with equal number of true and false values, so it fits the expected distribution. Each time taking the mean of those values is just an expansion of the integral calculating the expected value, as per definition of expected value.
Note that there is some recursion here. While the flip() handler is executing for the first invokation of flip(), another instance of the handler will execute for second and third invokations (inside resume true). I.e. it calculates the expected value by taking the mean of the expected values of each branch created by a flip. When a branch doesn't have any more flips inside, its expected value is taken to be the constant value that this branch returns.
Although I guess JIT compilers may be classified as systems programming, since you need to do some OS-specific tricks with virtual memory during execution of the compiler.
How kind.
> what does `for<'a>` and `+ '_` mean?
`for<'a>` allows you to introduce a new lifetime parameter which parametrizes an Fn argument independent of the lifetime parameters of the enclosing function/struct. Sounds complicated when you try to explain it in programming terms, but actually when you think about it in terms of logical quantifiers from maths (for<'a> being the universal quantifier introducing the 'a "variable", "for" -> "for all"), it's pretty simple. The idea is simple, the syntax is noisy. I've already complained about the noise in another comment: <https://news.ycombinator.com/item?id=31433427>
"+" in type constraints is just conjunction.
> C++ definitely gives you a head start when thinking about lifetimes, but there's still a huge difference between thinking about lifetimes, and writing them down to prove to a not-especially-smart borrow checker that they're ok. Obviously the latter is better, because it gives you guarantees. But it's still way way harder.
Getting a program to compile may be harder. But the whole task of writing a program long-term gets easier. Most of the complaints about borrow-checking I see online can be split into two categories:
1. syntax noise --- I agree
2. this program would work, if not for the borrow checker --- in almost all cases here people do not think about API boundaries at all. They look at the body of the functions they wrote and conclude that it should work. But they forget about one important thing: the compiler needs to ensure that changing the body of function f() without changing its signature should not break compilation of function g() which calls f(). That leads to examples where it seems as if something should compile, but doesn't, because of function boundaries which mandate that lifetimes should be correct regardless of function implementations. And there is a good reason for that: if I use a library, this library pushes out an update which doesn't change the types of anything, I don't want to be forced to update my code for it just to compile. So the work that ensures that everything stays composable throughout implementation detail changes is moved upfront.
The alternatives to "fighting the borrow-checker" are GC or difficult to debug (sometimes even to reproduce) bugs. Fixing the compilation is a lot easier for me than tracking down memory-corruption bugs I face in large C++ codebases with hundreds of contributors, so I disagree with the claim that making things work in Rust is harder.
> If it was your code they were talking about, you may feel differently.
I'm not the commenter you replied to, but no, I'm not my code, so I'm not going to get offended by someone criticising it. If someone does, it's on them.
There were a number of times in my career when someone called something I wrote overcomplicated, and I've never been even close to feeling any emotion about that fact at all. That remark always prompted me to hear carefully what they have to say about improving it. That's not a brag, just a certain level of emotional maturity.
People shouldn't be held responsible for every emotional reaction people have to their actions/words, because part of the responsibility for that rests on the person experiencing those emotions. In that case I don't think Casey said anything offensive, so if that made someone angry, they have themselves to blame for that.
When something doesn't involve a computer, I don't have the slightest problem staying focused. So when part of my job requires doing some maths on a piece of paper/window/whatever, when I want to read a book, when I engage in a hobby etc., I don't need to try hard to focus. It's only those bloody computers.
There are many reasons why linear history is important. Rather than saying that "you shouldn't want to do that", I'd prefer it if the tools people use were fixed to better serve the things people actually want to do.
A merge would only make sure that I don't need to resolve the same conflict multiple times. It wouldn't result in the conflicts not being generated in the first place, or them being saner. The only difference in conflict resolution between rebasing and merging is that the sides are flipped (what's shown on left in rebase, is on the right in merge, and vice-versa). Which doesn't address the second issue I listed: conflicts are not only repeated, but the conflicts themselves are pretty crazy. In my example, often-times C would never even touch files which were detected as having conflicting changes with A or B. It was pretty absurd that I would have a 3-line change in C, several hundred-line changes in A and B, yet the biggest conflicts would be triggered when rebasing C on top of B, and those would be in files C did not touch. Other times diffs in conflicts would have most of the lines added and removed actually identical, with only a couple in the middle different. Why would git mark them as conflicting, is beyond me. And then there was the issue of git not detecting conflicts, when it should have, instead merging changes in a way that broke source code. None of those issues is better handled by merging than by rebasing. Most of them however have pretty good solutions in a patch-based version control system, as opposed to snapshot-based like git (conflicts in files which weren't changed? not going to happen). An even better solution would be binary formats with domain-specific merge logic, like the one in KeePassXC.
As for ML functors: they're neither polymorphism, nor dynamic dispatch. First: functions for different types can't share names (unless we're talking about shadowing of names with lexical scope), the names are either qualified with the name of the struct they come from or they're not qualified at all. In all cases there is exactly one implementation for every name. And again: no dynamic dispatch. MLton even defunctorizes all code at compile-time. The only polymorphism in SML is the one that's used in some built-ins (like +), but it's not available to users of the language. SML has something called "polymorphic functions", but it has nothing to do with writing multiple implementations of one function under one name and letting the compiler select which one is called; which is what is usually meant by "polymorphism".
You confused XKCD with reality: <https://xkcd.com/1172/>
It's possible that in the future strace will use BPF as well to lower its overhead.
bpftrace is also more versatile. You could use it to e.g. collect stack traces for all the syscalls a program makes. You can also attach actions to more things than just syscalls. You can e.g. use kprobes to inject code into kernel functions which aren't exported as syscalls.
> This is undefined by the spec.
> There are 2 obvious ways.
> 1. Scan from the front, when you see an id for a record do the appropriate thing.
> 2. Scan from the back, find the end-of-central-directory-record and then use it to read through the central directory, only looking at things the central directory references.
I was recently bitten by this at work. I got a zip from someone and couldn't find inside the files that were supposed to be there. I asked a colleague, and they sent me a screenshot showing that the files were there, and that they didn't see the set of files that I saw. I listed the content of the zip using the "unzip -l" command. They used the engrampa GUI. At that point I looked at the hexdump of the file. What caught my eye was that I saw the zip magic number near the end of the zip, which was odd. The magic number was also present at the beginning of the file. At this point I suspected that someone used cat(1) to concatenate two zips together. I checked it with dd(1), extracting the sequence of bytes before the second occurrence of the zip magic number and the remainder into two separate files. And sure enough at that point both "unzip -l" and "engrampa" showed the same set of files, and both could show both zips correctly. Turns out engrampa was reading the file forwards, whereas unzip was reading the file backwards.
1. We have more ozone holes now.
2. We weren't constantly in direct contact with sunlight. If you move past very early stages, we had shelter in forests, caves, tents, igloos.
3. When we were in direct contact with sunlight, we had a darker skin, which is more resilient to that.
Correct me if I'm wrong.
I prefer Fork on Windows and Mac (prefer the Windows version for aesthetic reasons). Unfortunately, it's not available for Linux.
Emacs has nothing to do with shell prompts. It's a GUI editor as well. Emacs philosophy goes against Unix. I e.g. never use the terminal mode. Otherwise, your points seem on-point.
The article also suffers from the syndrome of taking a feature of a language and pretending that everything has to be seen through the lens of that feature. Well, newsflash: ownership semantics apply to only some things in Rust. They apply to values and bindings. But they don't apply to e.g. indices. I can take as create as many i's, where i=5, all referring to the same Vec, which later are going to be used to mutate the Vec, as I like, and Rust won't stop me. Similarly, there is Rc and Arc.
It also included one of those "wise" thoughts along the lines of "You know what? The true nature of computers/economy/life/love is..." - one of the templates of meaningless sentences. In this case it was "however virtually all computers are fundamentally mutable things". Hmm... Did we suddenly forget about pipelining, instruction ordering, caching, instruction-level parallelism, C/C++'s problems with memory aliasing? C is not a low-level language[1].
[1]: https://queue.acm.org/detail.cfm?id=3212479
If we were to write a statement analogous to the one advocated for in the article, but talking about mathematics instead, it could be "the fatal flaw of mathematics is that a lot of it is about reducing problems to linear algebra". How ridiculous does that sound?