Programming thread

  • 🔎 If you see an alert for analytics.usips.org, this is a self-hosted system I am running as a test. It is GDPR compliant and collects no invasive information.
  • Want to keep track of this thread?
    Accounts can bookmark posts, watch threads for updates, and jump back to where you stopped reading.
    Create account
nobody under 35 knows jack shit about how Linux works lately
Interest in "Systems" level understanding has never been so low. Mostly, I think this is because actually interfacing with Systems isn't done any more, everything runs off of an API, and sometimes at several layers of indirection out, like WASM routing audio thru PipeWire emulating PulseAudio which hooks into ALSA at the kernel level. Moving away from C makes this worse, and Rust also makes this worse, as instead of learning the Systems interfaces, the community reaches for """SAFE""" wrappers. The current trend of PCs becoming insanely expensive should bring a few folks back to seeing the benefits of low-level Systems-tier thought, but we'll see how that plays out in practice.
 
The current trend of PCs becoming insanely expensive should bring a few folks back to seeing the benefits of low-level Systems-tier thought, but we'll see how that plays out in practice.
I don't want to make any sweeping, prescriptive statements, but people are surprisingly lazy. Example: I know a guy who did an iOS dev bootcamp 10 years ago, got mad that they didn't teach Swift and refused to learn it himself for some inexplicable reason, so he went back to like repairing phones. Many such cases TBH. It's never been easier to learn anything computer, either.
nobody cares because its all about web and webapps now
Everybody wants to be a dev, nobody wants to know what Docker does under the hood. Or even AWS.
 
Now, I am not an American, so I am not fully aware of all the problems that you guys are facing there, but I highly doubt that these interview questions are intended to bring more Indians in your country.
I think you're underestimating just how much the managerial class hates American workers and loves having servile foreigners who can be deported and replaced if they complain.
 
Been hearing more and more greybeards complain about how nobody under 35 knows jack shit about how Linux works lately.
The thing is, about 20 years ago you were expected to install the OS and configure it on the dev machine and/or servers.
I may have said it earlier in this thread but if you couldn't figure out how to install Windows XP / Vista / Linux, then you failed the basic competency test.
 
The overwhelming majority of bugs aren't about memory safety like Rust makes it out to be and it's so fucking infuriating that all the zoomer faggots go I WANT NANNY COMPILER BORROW CHECKER TO MAKE ME FEEL SAFE that I'm about to just write assembly out of spite
I've been thinking about this a lot recently. It feels like the programming trends of today are entirely defined by the challenges of a couple of decades ago. I remember learning early Rust and being sold on it as the way to code highly parallel programs. Back in those days Rust had 3 pointer types, one which were just normal C pointers, one which was managed by the borrow checker (and so was thread safe) and one which had a reference counted GC. Now though, Rust evangelists are still talking about memory safety without mentioning concurrency, which is ostensibly the entire reason Rust has memory safety and borrow-checked pointers in the first place. We've of course largely solved concurrency in other ways, but there's still this idea that we should keep the failed attempt around and use it anyways.
 
Last edited:
What's a good mid level programming type job for someone who already has a science degree but little programming experience?

Is it web development, databases, cloud shit? I know little about this stuff but I'm looking to change careers soon.

Whenever I ask questions like this all the programmers say I shouldn't bother or that this or that language won't exist soon. Very unhelpful
With little to no programming experience, QA. (Word to the wise, it won't be mid level with no experience.) Specific language/tooling isn't all that important. Working QA you'll get exposure to technical knowledge of the systems you're testing and learn by osmosis, and the complex stuff demands mechanization so you'll get a chance to write scripts and practice your programming there. Programmers love a good, reliable QA guy.
 
I've been thinking about this a lot recently. It feels like the programming trends of today are entirely defined by the challenges of a couple of decades ago. I remember learning early Rust and being sold on it as the way to code highly parallel programs. Back in those days Rust had 3 pointer types, one which were just normal C pointers, one which was managed by the borrow checker (and so was thread safe) and one which had a reference counted GC. Now though, Rust evangelists are still talking about memory safety without mentioning concurrency, which is ostensibly the entire reason Rust has memory safety and borrow-checked pointers in the first place. We've of course largely solved concurrency in other ways, but there's still this idea that we should keep the failed attempt around and use it anyways.

It's even worse in a lot of ways.

Comedy interlude: Fil-C. "Make Rust Memory Safe Again." I KNOW that pissed Rust off. And I am fucking glad it did. Pizlo's own words: "Part of the reason why I'm doing this is I want to obviate the need for Rust. I'm not there yet performance-wise, but I will get there."

Gripes that just keep looping in my head:
1) Bounds checks are nearly free. You don't need gigaretard shit.
2) Handles just beat pointers at everything except "compilers easily can optimize retard shit" BUT YOU DON'T WANT TO FUCKING DO DIRECT POINTERS AS AN EXTERNAL API ANYWAY.

Here's some history that coooders of gender (and a lot of younger people here) don't know: OSes handed out handles, not pointers, in '71/'84/'85. Unix fds, Mac Toolbox, Win16. Every OS already knew better. Early 2000s, ECS (my beloved) reinvents it in userland: you give me a number, NOT A POINTER, a fucking id, and I do branchless cute bullshit behind it that you don't need to know about, here's your data or behavior.

The whole thing comes down to this: STOP USING RAW POINTERS UNLESS YOU HAVE A GOOD REASON, AND ONLY INTERNALLY ANYWAY. (References are just seasoned pointers!)

Rust feels like an over-wrought hammer that has nails for the wrong reasons justifying endlessly more nails and calling screws bigoted because meritocracy must be dismantled, and the tranny who wrote the manifesto saying to dismantle meritocracy won't say what replaces it. Because he doesn't know. Because he wants to say 'harm' and 'safe' and use 'tranny' as an immunization from scrutiny.

On concurrency: you win by avoiding shared mutable state, not by proving it safe. Channels, actors, immutable snapshots, stateless services, GPUs. The industry just went around the exact problem the borrow checker tries to figure out. ECS does it natively, because handles plus generations ARE snapshot semantics with ownership attached, and Bevy's parallel scheduler, Rust's own best concurrency story, IS an ECS!

Meanwhile perfectly correct concurrent structures still need unsafe. Lock-free SPSC ringbuf: the invariant is two lines of index arithmetic any human audits instantly, but ownership encoded in integers is invisible to the borrow checker. "Unsafe code harms users," so you clone everything and call it 'safe' while using your fidget spinner and wearing your fucking programming socks. Or call it Unsafe{}.

Can someone can tell me what pointer chasing and piles of references actually buy?

ECS emulates almost any topology. Parent/child components give you trees. Hashing is just a hash, it's already a list. Segmented arrays (AoSoA) are still O(1) random access, one shift and one mask, while the pointer structure's O(1) is per-hop and every hop is a 200-400 cycle cache miss and you can't pre-fetch with pointers unless you want the security bugs Apple stumbled on for trying.
 
So, to be clear, as my autism struggles with overloaded initialisms like ECS, you're referring to https://en.wikipedia.org/wiki/Entity_component_system ?

Yep! If you're used to OOP your mind will fight it. If you do data centric/data oriented/data flexible programming it'll feel like coming home.
You just fucking stack arrays. that's literally it. SoA is just that. (Struct of Arrays). AoSoA is chunking the struct of arrays into 16kbish (that's just what benches fastest). The handle is an id which turns into an index.

It's also quite literally 5NF. Perhaps 6NF. I'm not a DB guru and I've seen both.
 
Interest in "Systems" level understanding has never been so low.
It doesn't help that Systems Software Research is Irrelevant according to Rob Pike and the other UNIX weenies who helped to kill it. Until now, I was only familiar with the title, but I found it only takes a few minutes to read. He seriously thinks Plan 9 to be something meaningfully distinct, just like the kind of guy who thinks his C-flavoured ALGOL clones are meaningfully different.

I found a funny line in here:
Rob Pike said:
The future is distributed computation, but the language community has done very little to address that possibility.
He mentions Perl, but not Erlang in his little paper. :story:
Now Erlang had only left Ericsson for a year at that point, but still, and it's not as if Erlang was the first language to do what it did.
Mostly, I think this is because actually interfacing with Systems isn't done any more, everything runs off of an API
Yes, this is how a proper system is designed. Projects such as the Linux kernel make people think bit-banging is the proper way to request something of hardware, when high-level messages are a much better method. Really, the entire model of drivers rather than explicitly making the hardware act like an independent object is another conversation.
Moving away from C makes this worse, and Rust also makes this worse, as instead of learning the Systems interfaces, the community reaches for """SAFE""" wrappers.
I've written an Ada binding to POSIX Sockets without involving any C language code on my side. Even with Ada's standardized facilities for interoperating with it, the experience was still terrible: I had to wade through header files just to find basic shit like type definitions and the numerical values of what pass for macros. I wrote a low-level binding that basically wrapped around the C language procedures (socket, bind, recvfrom, and sendto) while making them a bit more convenient; I then used that library to write a high-level package that exposed an entirely different interface, one actually pleasant to use. There's nothing about the hardware that makes POSIX Sockets necessary, it's just in the way, because it's been there for nearly half a century. That's it.
I may have said it earlier in this thread but if you couldn't figure out how to install Windows XP / Vista / Linux, then you failed the basic competency test.
Here's a reminder that Richard Stallman has never installed GNU/Linux and doesn't know how. He could figure it out, certainly, but what use is that knowledge, really?
It feels like the programming trends of today are entirely defined by the challenges of a couple of decades ago.
I completely agree.
I remember learning early Rust and being sold on it as the way to code highly parallel programs.
Alan Kay had children writing massively parallel programs without issues a long time ago, while the average C++ programmer shits himself with not even sixteen threads of execution.

Here, I've got a real programming question for the thread this time. I've written the following C11 code in an attempt to get some information out of the compiler, which I liken to squeezing blood from a stone:
C:
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>

int
main ()
{
  ssize_t s;
  socklen_t t;

  if (2 == SOCK_DGRAM)
    printf("SOCK_DGRAM = 2\n");
  else
    printf("SOCK_DGRAM /= 2\n");

  if (2 == AF_INET)
    printf("AF_INET = 2\n");
  else
    printf("AF_INET /= 2\n");

  if (32 == MSG_TRUNC)
    printf("MSG_TRUNC = 32\n");
  else
    printf("MSG_TRUNC /= 32\n");

  if (_Generic(s, long: 1, default: 0))
    printf("ssize_t is long\n");
  else
    printf("ssize_t isn't long\n");

  if (_Generic(t, unsigned int: 1, default: 0))
    printf("socklen_t is unsigned integer\n");
  else
    printf("socklen_t isn't unsigned integer\n");

  return !((2 == SOCK_DGRAM)
           && (2 == AF_INET)
           && (32 == MSG_TRUNC)
           && (_Generic(s,         long: 1, default: 0))
           && (_Generic(t, unsigned int: 1, default: 0)));
}
In Common Lisp, I've got functions like INSPECT, DESCRIBE, and TYPE-OF that make this utterly basic task as simple as it should be. The C language, lacking any vocabulary with which to discuss itself, has nothing. I looked into getting LLVM to spit out useful information, and was greeted with a lot of automatically generated documentation for hundreds of C++ language routines. Notice the best I could do here is use _Generic, which is what Common Lisp calls TYPECASE, to see if I'd included the true type, but I've got no means by which to figure out the type unless I already know every type it could be.

Now, an acquaintance wrote his own POSIX Sockets binding for Ada, and just wrote enough C language routines to ignore all of this shit, by having the routines accept a new type kept in sync between them, and only returning simple Int return codes, but I knew I could do better. My library works, and has worked for years, but this would be a pain if I ever cared about someone else using it. Does anyone here know a way to get what I want?
 

Attachments

The thing is, about 20 years ago you were expected to install the OS and configure it on the dev machine and/or servers.
I may have said it earlier in this thread but if you couldn't figure out how to install Windows XP / Vista / Linux, then you failed the basic competency test.
Infrastructure has always been the competency test, as it's what leads to system design which inevitably affects architecture. If you can't get docker or your IDE up and running, how can I expect you to spin up a kubernetes cluster, and get my network running.
 
Infrastructure has always been the competency test, as it's what leads to system design which inevitably affects architecture. If you can't get docker or your IDE up and running, how can I expect you to spin up a kubernetes cluster, and get my network running.
Idk if you are joking but this would absolutely be a joke at work lol
 
He mentions Perl, but not Erlang in his little paper.
Now, I do like Perl a little bit, but how is Perl related to distributed computations? I do not get it. I have always viewed the Perl language as a tool for text analysis and nothing else. Although I did not read the paper.

Projects such as the Linux kernel make people think bit-banging is the proper way to request something of hardware, when high-level messages are a much better method.
What is wrong with bit masks, and what kind of alternative do you propose?

Here, I've got a real programming question for the thread this time. I've written the following C11 code in an attempt to get some information out of the compiler, which I liken to squeezing blood from a stone:
Normally one would not need to know the exact value of a macro. If you do need to know - the proper way is to read the header, but I admit that this is a major pain in the ass.
I just print it, when I need to know.

Regarding the type of a macro - sizeof kinda comes in handy. If it's 4, then it's most certainly a 32-bit int. Not perfect, but does the job.
Also the manual helps. man open.2 for example.
 
Last edited:
Here, I've got a real programming question for the thread this time. I've written the following C11 code in an attempt to get some information out of the compiler, which I liken to squeezing blood from a stone:

In Common Lisp, I've got functions like INSPECT, DESCRIBE, and TYPE-OF that make this utterly basic task as simple as it should be. The C language, lacking any vocabulary with which to discuss itself, has nothing.
That's because when you're writing in a high-level language such as C, you generally don't care about the low-level details of the implementation.

Is it really the fault of C if the linux kernel expresses itself via a series of nested C headers? printf("%d\n", SOCK_DGRAM); works. These sorts of things are very unlikely to change because everything will break if they do. SOCK_DGRAM is 2, just as a byte is 8 bits wide. If you are ever in a position where these truths are not so, you probably have more important things to worry about.

The strange integer types are also explained in man pages; ssize_t is a "signed integer type capable of storing values at least in the range [-1, SSIZE_MAX]" (in practice, as wide as the address bus) and socklen_t is "an integer type of at least 32 bits" which effectivley means that it is 32 bits.

You can also just do something that the compiler will complain about if you absolutely must know the exact type:
Code:
error: initialization of ‘void *’ from ‘unsigned int’ makes pointer from integer without a cast [-Wint-conversion]
    7 |         void *x = (socklen_t)1;
 
Now, I do like Perl a little bit, but how is Perl related to distributed computations? I do not get it. I have always viewed the Perl language as a tool for text analysis and nothing else. Although I did not read the paper.
He mentions Perl elsewhere in the little paper is all I meant by that.
What is wrong with bit masks, and what kind of alternative do you propose?
What I mean is a language like Ada allows the programmer to specify a type and how it's organized physically, so when setting fields the compiler handles generating the shifts and masking. The Linux kernel guys just do all of that manually, which is stupid.
Normally one would not need to know the exact value of a macro. If you do need to know - the proper way is to read the header, but I admit that this is a major pain in the ass.
Agreed. Here's a little revealer script I've got that, sometimes, handles a small part of this issue:
Bash:
#!/bin/sh

printf "$1" | gcc -D_GNU_SOURCE \
-include fcntl.h \
-include sys/syscall.h \
-include sys/mman.h \
-include sys/types.h \
-include sys/socket.h \
-include signal.h \
-include sched.h \
-include limits.h \
-include linux/futex.h \
-include linux/fs.h \
-include time.h \
-include poll.h \
-E - | tail -n 1
The problem with this is illustrated by these two interactions:
Bash:
~ $ ./reveal AF_INET
2
~ $ ./reveal MSG_TRUNC
MSG_TRUNC
Several of these macros are recursive, redefine other macros, or use some other method that makes them impenetrable so.
Regarding the type of a macro - sizeof kinda comes in handy. If it's 4, then it's most certainly a 32-bit int. Not perfect, but does the job.
Yeah, I thought of that, but I don't know if that necessarily works. There are all sorts of little rules about how different types get passed in registers on certain machines. The whole thing's a mess.
If you are ever in a position where these truths are not so, you probably have more important things to worry about.
Yes, I've reviewed the GNU and BSD implementations: Both AF_INET and SOCK_DGRAM are always two, but MSG_TRUNC isn't consistent, and even the sockaddr struct has an annoying difference between them. The IPv6 macros are totally inconsistent, but it's not like I was itching to support IPv6 anyway. :story:
The strange integer types are also explained in man pages; ssize_t is a "signed integer type capable of storing values at least in the range [-1, SSIZE_MAX]" (in practice, as wide as the address bus) and socklen_t is "an integer type of at least 32 bits" which effectivley means that it is 32 bits.
Yeah, socklen_t is just a type they added years into it, where it was unsigned int earlier, so they could pretend they'd provided an abstraction.
You can also just do something that the compiler will complain about if you absolutely must know the exact type:
Ah, now that would be useful. I appreciate the tip.
 
What I mean is a language like Ada allows the programmer to specify a type and how it's organized physically, so when setting fields the compiler handles generating the shifts and masking. The Linux kernel guys just do all of that manually, which is stupid.
C allows you to do that with bitfields in a struct. Unfortunately, the standard doesn't specify whether the fields occupy increasingly significant or decreasingly significant bits of the underlying machine word, so it's implementation-dependent.

So
C:
struct PACKED {
    unsigned a : 8;
    unsigned b : 4;
    unsigned c : 4;
    signed d : 16;
};
struct PACKED p;
/* ... */
p.b = x;
might be equivalent to
C:
typedef unsigned int struct_PACKED;
struct_PACKED p;
/* ... */
p = (p & 0xFF0FFFFF) | ((x << 20) & 0x00F00000);
assuming that the compiler starts allocating fields from the most signifcant bit and working downward.
 
C allows you to do that with bitfields in a struct. Unfortunately, the standard doesn't specify whether the fields occupy increasingly significant or decreasingly significant bits of the underlying machine word, so it's implementation-dependent.

So
C:
struct PACKED {
    unsigned a : 8;
    unsigned b : 4;
    unsigned c : 4;
    signed d : 16;
};
struct PACKED p;
/* ... */
p.b = x;
might be equivalent to
C:
typedef unsigned int struct_PACKED;
struct_PACKED p;
/* ... */
p = (p & 0xFF0FFFFF) | ((x << 20) & 0x00F00000);
assuming that the compiler starts allocating fields from the most signifcant bit and working downward.
De facto standard is it depends on the endianness of the cpu
 
Back
Top Bottom