dpryden
It says:
> I give the following general definitions for strong and weak typing, at least when used as absolutes:
> Strong typing: A type system that I like and feel comfortable with
> Weak typing: A type system that worries me, or makes me feel uncomfortable
It did make me go pick up an old copy of PC Magazine from 1995 from the other side of the room and start leafing through it, though.
In any case, though, my understanding is that the technical capacity for this has existed for nearly 10 years now, just behind a configuration setting. So it's possible that this change is just a code cleanup. It's also possible that someone is trying to increase the percentage of searches that have location information, that doesn't seem terribly far-fetched either, and I can imagine lots of ways people could try to rationalize it as actually benefiting users. (Whether it actually does benefit users is of course debatable.)
The first edition of K&R was published in 1978.
Wikipedia says that OS/360 had a notion of threads as early as 1967, although the distinction between threads and processes wasn't as clearly defined at that time.
In 1978, threading and concurrency was still an area of academic research. There are some great papers from Dijkstra and Lamport from that era, but much of the research at that time considered each concurrent "thread" to be a separate program; that is, each process was conceptually single-threaded, and if you wanted to do multiple things you made multiple processes for them.
The idea of multiple mini-processes inside a single process came later and took a while to stabilize into its modern form. POSIX didn't standardize thread-related APIs until 1995 and Linux didn't get modern POSIX threads support until Linux 2.6 in 2003.
I'm not saying that GC is always the best choice, but this article gets the most important argument wrong:
> 1. Updating reference counts is quite expensive. > > No, it isn't. It's an atomic increment, perhaps with overflow checks for small integer widths. This is about as minimal as you can get short of nothing at all.
Yes, it is. Even an atomic increment is a write to memory. That is not "about as minimal as you can get short of nothing at all".
Additionally, every modern GC does generational collection, so for the vast majority of objects, the GC literally does "nothing at all". No matter how little work it does, a RC solution has to do O(garbage) work, while a copying GC can do O(not garbage) work.
Now, that's not to say that GC is automatically better. There are trade-offs here. It depends on the workload, the amount of garbage being created, and the ratio of read to write operations.
The article says:
> I've already stated I'm not going to do benchmarks. I am aware of two orgs who've already run extensive and far-reaching experiments on this: Apple, for use in their mobile phones, and the Python project.
I can counterpoint that anecdata: Google extensively uses Java in high-performance systems, and invented a new GC-only language (Go) as a replacement for (their uses of) Python.
The right answer is to do benchmarks. Or even better yet, don't worry about this and just write your code! Outside of a vanishingly small number of specialized use cases, by the time GC vs RC becomes relevant in any meaningful way to your performance, you've already succeeded, and now you're dealing with scaling effects.
That said, I think I tend to use "upstream" in a similar sense to how package maintainers talk about packages: "upstream" is the source (where the thing originally came from) and "downstream" is the sink (where I will send it to). This implies that "upstream" for a request is "downstream" for a response, and vice versa. I don't know if that's necessarily correct usage, but that's how I've done it.
Knowing how to write a topological sort isn't the key skill here (although, I would argue, it's a good skill to know, and it's probably much simpler than you're imagining). The key skill is knowing that a topological sort will solve the problem, or perhaps it's simply knowing that "finding an order of actions that satisfies these dependency constraints" is called a topological sort.
I have always been under the impression that the goal of leetcode-style interviewing was actually to measure a candidate's ability to recognize what algorithmic tool to use, rather than to measure a candidate's ability to implement an algorithm from scratch. When I've been in the position of interviewing in the past, I've always been more interested in that problem-solving approach than in the actual code.
In my experience, most software jobs are 90% figuring out the problem that needs to be solved and coming up with tactics for solving the problem, and 10% actually implementing those tactics. In that context, investigating the ability of a candidate to analyze problems seems like an excellent interview technique.
To me it seems similar to how "auto" as a noun is generally short for "automobile", but most people are aware that other things can also be called "auto". When a camera says it is "auto focus" I cannot imagine that any normal person would assume that phrase has anything to do with automobiles.
It is incredibly common for the same word to have different meanings in different contexts. I personally have literally never had a conversation about cryptocurrency in which any person used the word "crypto" to mean "cryptocurrency", so I am clearly out of this loop. But if people decide to use it that way as slang in a certain context it certainly doesn't change the meaning of related words, or even mean it's impossible to use a different slang meaning in different contexts.
As an example: imagine I am developing my application locally, at http://localhost:8080/ , and this application supports an OpenID Connect identity flow with an identity provider, https://idp.corp.example . Today I can test the login flow by telling the IDP that localhost:8080 is a valid URL to redirect back to, so that I can click a "login" button in my application, log into the real IDP, and get a token posted back to something like http://localhost:8080/idp-callback . This makes it easy to develop a system locally that also communicates with various backend microservices which require authentication with a common IDP.
I can't imagine that this is a rare scenario: it seems pretty normal to me. But if I understand the proposal, it sounds like the long-term goal is to prevent this kind of environment for local development, and instead force you to either run your dev stack remotely (with a public IP!) or else run your entire IDP stack locally (so that it has a local IP). Neither of those seem like good ideas to me.
Of course, the other option would be to modify local dev servers to accept CORS preflight requests and respond correctly, but I'm always slightly uncomfortable adding code into a local dev stack that would be unsafe if enabled in production. At very least it makes it harder to debug when something inevitably goes wrong with this API flow.
There are probably ways to solve this by introducing more proxies into a local dev stack, but I worry that these kinds of little papercuts will just make development that much harder for microservices-architecture applications, which are already hard enough to develop and debug as it is.
In either case, though, it's an inside joke precisely because it's more relatable to those who are (or were) inside. In particular, I think it would be most funny to someone who was at Google about a decade ago; when I left Google in 2017 things had already changed enough that this didn't ring quite as true for new hires.
That said, GCP is not very representative of what the internal platform looked like circa 2010. (Or even of what the internal platform looks like now, as far as I know.)
Noogler: Haha, this video is so funny!
L4 SWE: (Crying because the video is so true)
L5 SWE: Haha, this video is so funny! I should show it to my interns, this will be a good training for them.
L6+ SWE: Why do people think this is funny? This Broccoli Man guy makes some really good points...
This whole field of scraping and anti-bot technology is an arms race: one side gets better at something, the other side gets better at countering it. An arms race benefits no one but the arms dealers.
If we translate this behavior into the real world, it ends up looking like https://xkcd.com/1499
I am not aware of any language (outside of intentionally-minimalist esolangs) that doesn't support floating point numbers. In some languages (like JavaScript) that's the only kind of number you get.
C is simple, to what today is nearly an absurd degree, but it at least integrates well with an enormous ecosystem of existing tooling and libraries. Go, by contrast, feels the need to re-invent every possible wheel, often with a seemingly intentional effort to be different just because.
C ABIs are rock solid on every platform. Any language or tooling in the world can load functions from a C library. By contrast, try calling Go from another language. You will quickly give up and use network protocols or subprocesses instead.
The converse is true too: on major platforms, the C library defines the operating system more than the kernel does. (I mean, in a Unix sytem, the entire libc is documented in the man pages. Documentation for writing C is literally built into the operating system.) By contrast, Go decides to avoid the standard platform ways of calling every operating system function, and instead implements syscalls directly into the kernel, basically just because Go hates interoperating with anything that's not Go.
C is a good language not because it is a good language, but because it is a good ecosystem. Unix and C are closely intertwined. (Even Win32 and C are pretty closely intertwined!) I could imagine a Go-based Plan9-like system -- if that existed, and had decades of engineering behind it, I could see the argument being made that Go's ecosystem compares to C's. But while Go may aspire to that, it isn't anywhere near there yet.
And when you start comparing Go with other languages on the basis of language features, I think Go fails laughably more often than not. (I'll give you "C++ is a clumsy mess", though.) But compare Go with Rust, with C#, with Haskell -- for that matter, compare Go with Java and I think Go is a less powerful, less useful language to write real code in.
If I want a lightweight, quick-and-dirty program, I'll use Python. If I want to write a reusable library that gets plugged into other software, I'd go with C (or at least write the interop layer in C). If I want a high-performance server, these days I would probably go with Rust. If I want a pragmatic, well-rounded language to write a large project in, I think C# would probably be my first choice.
What space does that leave for Go? Outside of microservices (where I contend that Rust is a much better choice, and there are lots of other options), Go's niche seems to be command-line tools. I sort of understand that, because Go's static linking makes redistributing binaries relatively easy (although Go by no means has a monopoly on that). But in the space of command-line tools, Go's argument handling is frankly bizarre: it looks at decades of precedent about what users expect from argument syntax and behavior and decides to throw it all away and reinvent the wheel.
I guess that means this comment (rant?) has ended up where it started: Go defies convention, basically "just because". By contrast, C defines convention. "If you like C, you will love Go"? Nope, not at all.
PC: "Paper Cassette" Load: "out of paper, please load more" Letter: "US Letter size (8.5"x11")"
In some offices you'd be equally likely to see similar codes like "PC LOAD A4" or "PC LOAD LEGAL".
The LaserJet Series II and III didn't have paper drawers, but rather paper cassettes, which you could load with a stack of paper (maybe a few hundred sheets? less than half a ream IIRC), and then swap in and out of the machine as a unit.
Some models (I think it might have been an add-on peripheral for the Series II?) had two cassette slots, so you could load two supplies of paper at the same time. Either you could load the same size twice, in which case it would perform like a backup: if you ran out of paper in the first cassette, it would switch to the second cassette automatically but start flashing a light to tell you that it was time to reload the first one. Or you could load two different sizes, and it would select the right cassette to draw from based on the size of the document being printed.
The Series II didn't have a way to measure the size of the paper, so each cassette was designed for only one paper size. There was an interface where the cassette plugged into the printer which indicated which size paper it contained.
Due to the design of the paper cassette, it was very hard to tell from the outside how much paper was left. There was a tiny window but it was nearly opaque. IIRC later versions of the cassette improved this.
So if you had a model with only one cassette (which was fairly common, I think) and you loaded a "US Letter" size cassette, and it ran out of paper, the printer would refuse to print anymore until you gave it more paper. In the meantime the tiny LCD screen would flash "PC LOAD LETTER".
The printer was a workhorse and I can testify that many offices in my area were using 1980s-era Series II printers daily in 1999, when I had a part-time IT job that among other things involved doing maintenance on said printers. Replacing the rubber paper pickup roller and clearing paper jams out of the fusing unit were probably the two most common trouble cases, IIRC.
I don't see any "about this site", "who created this site", etc. I don't see any information about who authored a particular snippet. Perhaps more information is available if you log in? But it seems like it should be possible to evaluate the trustworthiness of a site _before_ sharing information with it, so I don't feel comfortable interacting with the site as it is.
In my opinion, squash merges make the most sense when the following are true:
1. Developers cannot push directly to shared branches; all commits into shared branches are done via pull requests with mandatory code review.
2. Shared branches must build and pass tests at every commit.
3. The team builds features incrementally and uses the "Branch by Abstraction" approach (feature flags/experiments) to ensure functionality can be merged before it is ready to be enabled in production.
4. Changes that merge into the shared ("trunk") branch are released frequently.
Now, you may argue against some of those practices, and if you end up in a different place on one of those, squashes may not make sense to you. But then the actual difference of opinion is elsewhere. It's not really about squashing, it's about how much code is a reasonable granularity to code review at one time.
If you can keep the code review cycle tight, then a pull request branch basically becomes almost like a virtual pair-programming session. There may be some back and forth as the author and reviewer settle on a final form of the commit. But I don't see the commits that happen along the way as valuable in the least. (In my experience, the vast majority of intermediate commits are things like "fix typo", "make linter happy", "rename this function because code reviewer pointed out it was named inconsistently", etc.) Instead, my mental model is that a pull request is like a mutable commit: you keep mutating it until it's the commit you want, and then you merge it. Since my mental model is that a pull request is a kind of commit, it makes sense that when it merges in, it does so as a single commit.
Again, I'm not saying that this is the only valid way to work. But neither is it an invalid way to work.
As a meta-comment: When you see someone else making a technical decision that doesn't make any sense to you, rather than instantly assuming that they are a clueless incompetent, it's usually more instructive to assume that the decision does make sense to them, and to try to figure out what about their circumstance is different from yours to make that the case.
I can see a place for managing OS updates on an instance, but that seems more like "pets" than "cattle"... and I've always treated Kubernetes nodes like cattle, not pets. Isn't that the most common approach anyway?