HN user

pdmccormick

389 karma

Hi! My name is Peter, and I'm a software developer primarily focused on embedded Linux, systems programming and security.

GitHub: https://github.com/pdmccormick

[ my public key: https://keybase.io/pdmccormick; my proof: https://keybase.io/pdmccormick/sigs/OrKXSd2zp2Th8eFGRrxu1hQKb9wH0tnQel0Yd089mU0 ]

Posts2
Comments101
View on HN
Just Use Go 3 months ago

This also works:

    var _ MyInterface = (*MyStruct)(nil)
In my mind as I read this, I am reminded that it is the receiver type *MyStruct that implements MyInterface. YMMV.
Our principles 3 months ago

Also, if you kill 10 people a year, you will have an accelerating rate of average wealth growth!

"Become close friends with the software you use all the time." That is a beautifully evocative phrase for a lovely idea, thank you for sharing.

If I may share an idea for which I don't have nearly as nice and succinct a summation, but I've come to view my personal computing environments through the lens of being a garden. I spend so much time within them, working, learning, playing and writing. I can see the different seasons of my life reflected through naming conventions, directory structures, scripts I've written and bookmarks I've long ignored. There are new things I want to try and explore in the spring when I hopefully have a bit more free time. I have planted seeds while children slept in my arms or in the next room, and I have enabled their dreams with the fruits of my labour. I would even say I have occasionally communed with the close and holy darkness on long, late nights.

In time everything I have created will return to dust, and probably no one will ever know this garden as I have. But it has still been a place of growth and blessing.

Conceptually, can you break your processing up into a more or less "pure" functional core, surrounded by some gooey, imperative, state-dependent input loading and output effecting stages? For each processing stage, implement functions of well-defined inputs and outputs, with any global side effects clearly stated (i.e. updating a customer record, sending an email) Then factor all the imperative-ish querying (that is to say, anything dependent on external state such as is stored in a database) to the earlier phases, recognizing that some of the querying is going to be data-dependent ("if customer type X, fetch the limits for type X accounts"). The output of these phases should be a sequence of intermediate records that contain all the necessary data to drive the subsequent ones.

Whenever there is an action decision point ("we will be sending an email to this customer"), instead of actually performing that step right then and there, emit a kind of deferred-intent action data object, e.g. "OverageEmailData(customerID, email, name, usage, limits)". Finally, the later phases are also highly imperative, and actually perform the intended actions that have global visibility and mutate state in durable data stores.

You will need to consider some transactional semantics, such as, what if the customer records change during the course of running this process? Or, what if my process fails half-way through sending customer emails? It is helpful if your queries can be point-in-time based, as in "query customer usage as-of the start time for this overall process". That way you can update your process, re-run it with the same inputs as of the last time you ran it, and see what your updates changed in terms of the output.

If those initial querying phases take a long time to run because they are computationally or database query heavy, then during your development, run those once and dump the intermediate output records. Then you can reload them to use as inputs into an isolated later phase of the processing. Or you can manually filter those intermediates down to a more useful representative set (i.e. a small number of customers of each type).

Also, its really helpful to track the stateful processing of the action steps (i.e. for an email, track state as Queued, Sending, Success, Fail). If you have a bug that only bites during a later step in the processing, you can fix it and resume from where you left off (or only re-run for the affected failed actions). Also, by tracking the globally affecting actions you can actually take the results of previous runs into account during subsequent ones ("if we sent an overage email to this customer within the past 7 days, skip sending another one for now"). You now have a log of the stateful effects of your processing, which you can also query ("how many overage emails have been sent, and what numbers did they include?")

Good luck! Don't go overboard with functional purity, but just remember, state mutations now can usually be turned into data that can be applied later.

That seems like a lot of money. How quickly can sustainable capacity be built up in terms of building power plants, data center construction, silicon design and fabrication, etc.? Are these industries about to experience stratospheric growth, followed by a massive and painful adjustment, or does this represent a printing press or industrial revolution like inflection point?

Would anyone like to found a startup doing high-security embedded systems infrastructure? Peter at my username dot com if you’d like to connect.

“It sounds like a bunch of DJ’s dared each other to set their drum machines to BPM=1000”

That has been my favorite line from this for decades (at least that’s how I remember it going).

Could you be more specific about where you think the brain rot is? I thought the issues regarding bcachefs and Linux kernel development revolved around respecting conventions for code freezes and release candidates. It seemed more about getting along socially than technical objections to the technology.

I love projects like these. They touch upon so many low level aspects of Unix userlands. I appreciate how systemd ventured beyond classical SysV and POSIX, and explored how Linux kernel specific functionality could be put to good use. But I also hope that it is not the last word, and that new ideas and innovations in this space can be further explored.

Recently I implemented a manufacturing-time device provisioning process that consisted of a Linux kernel (with the efistub), netbooted directly from the UEFI firmware, with a compiled-in bundled initramfs with a single init binary written in Go as the entire userland. It's very freeing when the entire operating environment consists of code you import from packages and directly program in your high level language of choice, as opposed to interacting with the system through subprocesses and myriad whacky and wonderfully different text configuration files.

I wonder how many more orders of magnitude of precision will be realistically possible. I wonder if we'd ever be able to use gravity to "see" things at non-cosmological scales, like if you could resolve the gravitational waves and interference patterns caused by a person walking by.

I remember studying the source code to the PHP Zend Engine 25 years ago, and seeing a triple C pointer for the first time (I think it was `zval***`?) I did a lot of PHP in the following years, including using PHP for a high school programming contest (but my submission was rejected because the PHP language and using it in a standalone CLI context in particular was unfamiliar to the staff). I appreciate what it enabled me to accomplish in that era.

Not to mention intact families where parents had sufficient discretionary time (i.e. jobs that paid the bills with reasonable weekly hours) and a culture of prioritizing passing down knowledge to children and creating spaces in the home for them to pursue their individual interests and talents. People are not just atomized economic units.

This reads like an LLM's fever dream. Maybe the singularity won't be a unitary super-intelligence but rather something like a gaggle of backscratching consultants, a self-perpetuating, invasive, seething mass of bureaucratic AI agents that are always working hard to convince management that the solution is always more AI, especially for the problems created by earlier, less sophisticated, AI's.

I'm genuinely curious, for someone who develops web application backends and larger distributed systems & infrastructure, predominantly using Go and Python, exclusively targeting Linux, is there anything in the .NET ecosystem that anyone would recommend I take a look at? Many thanks.

By "personal computing" I guess I do mean machines that I have full root on, so usually either a laptop/desktop or a local headless server.

Out of curiousity, these servers that you're dealing with, do you have to use them for long/focused work like writing or software development, or are these more like sysadmin interactions? Have you noticed any repeated commands or sequences you find yourself running over and over? Is there any opportunity for you to write local scripts that then SSH into the targets and run those commands on your behalf? Or locally use sshfs and manipulate files like they were local.

I'm curious, how much time would you say you spend on a set of personal machines, versus connecting to random hosts?

My conceptualization of "personal computing" is something more akin to a home or a garden. It's a place where I should feel comfortable and relaxed. Familiar. It's a refuge of sorts, a place where I will inevitably spend an inordinate amount of time, and actually have a lot of real life experiences reflected (through the form of personal writing). Whether working, reading or writing, I tend to think of these machine contexts really as extensions of and foils for my mind. A discoverable mind palace, if you will.

I have not tried that yet, no. For me the goal isn't just about shortening often repeated commands, as though the limitation were purely about character input speed, it's more of a contemplative practise of learning how to notice and respond to that internal prompt that says "I wish I could say that more succinctly" or "There has to be a better way to do this". And then either telling my mind work on those questions, or being open and receptive to coming across something relevant on the topic, or seizing a moment of inspiration to write a time-saving script or come up with a meaningful (to me) alias.

Thanks, 25 years in and I'm still learning new things. But I don't think I was specifically shouting out the Unix philosophy. My comment was more in response to the OP article, that rather than "trying" to optimize in the sense that you already know what you need to optimize for and towards, instead thinking of self-improvement as being a by-product of being able to think new thoughts, thus the upfront emphasis on the acquisition of new language (with a big nod to the Sapir-Whorf hypothesis). This could be through collecting good terminology, best practise and patterns, and learning the philosophies of systems such as Unix and languages like Go and Python. However, with aliases and scripts, it becomes something you can actually "speak" in an effective sense, which ultimately can lead to improvements over earlier capabilities that go beyond what you could have envisioned as possible from the start.

I took a handful of math courses in school, and what I didn't realize till later was that learning how to rigourously prove things mathematically had dramatically reshaped my mind. I am left wondering what it must be like for the minds of those who have traversed that tower far higher than I ever have or will.

Over the past year, I’ve made a deliberate effort to improve my “personal computing”, which for me is about how I use the shell. I start by trying to notice when I’m doing the same task multiple times and getting tripped up on the specific syntax or sequence of commands (i.e. what’s the Git command again to split certain files out of a previous commit into a separate one?) I will then create an alias, a shell script or maybe a larger program to start addressing it, solving my initial use case first, and gradually revisiting things over time to make improvements.

Through this process I’ve created a whole idiom of commands for myself. I have no pretense that they would be useful to anyone else, and I don’t sweat them beyond their ability to make my life easier. From these incremental efforts, I find I have access to a new kind of language in my mind. Especially with Git, I find myself seeing it as an extension of my mental process, integral to how I even think about solving problems, not just a VCS I use to push code around.

By noticing how I was working, I didn’t seek to optimize where I was at, I sought to graduate to levels where what previously needed optimization just doesn’t exist.

Pivoting to the related subject of compact textual representations of _typed_ data, check out CESR[0]. Through some careful choices of code prefixes and payload lengths that won't require Base64 padding, it provides a novel encoding scheme for JSON plus cryptographic signatures (all while enabling true concatenative composability). It is at the heart of the KERI[1][2][3], a decentralized identity management scheme, and ACDC[4], a mechanism for verified credentials.

[0] https://trustoverip.github.io/tswg-cesr-specification/draft-... [1] https://keri.one/keri-resources/ [2] https://trustoverip.github.io/tswg-keri-specification/draft-... [3] https://github.com/WebOfTrust/keripy/ [3] https://trustoverip.github.io/tswg-acdc-specification/draft-...