Posts in Programming (20 found)

Which Rude is it?

The commerical airport we use here in Bend, Oregon is actually in Redmond, Oregon. Flights from here generally depart very early. It think it’s because they need to make it to bigger airports to make connections to further-away places. Flight typically depart at 4:30-6:30 AM. They want your bags an hour before departure, and the airport is 30 min from Bend, so you gotta be out the door sometimes at 3:00 AM meaning ungodly 2:30 AM alarm clocks. That’s the extreme case though. If you aren’t checking a bag and you’ve got a 6:00 AM flight, maybe you’re leaving the house at a spicy but tolerable 4:45 AM. That was too much preamble for this, but now you know. The one giftshop/coffeeshop in the airport opens at 4:00 AM. One person opens it up and starts selling things to the couple hundred people milling around in the one terminal preboarding area. This shop sells all the normal stuff you see in airport giftshops like cheezy Central Oregon sweatshirts and magnets, cold beverages and string cheese, magazines, and the like. They are also, and perhaps mainly, a coffeeshop. People stand in line to buy coffee. It’s early in the morning. You can’t bring in liquids. It’s damn coffee time. Right in the heat of the morning airport action, there might be 20-30 people in line. It’s a whole thing. Now we’ve arrived at my point. What do you order from this one person working at this coffeeshop at 4:00 AM? You can’t help but be aware there are 20 people behind you in line and how there is one person taking orders and making the coffee drinks. Right?! You could order a latte, which will take like 3 minutes to make. Or you could order a drip coffee in which this person hands you a cup in 3 seconds. My brain is built such that I cannot possibly order something that will take this person a while to make. Like the words would be unable to come out of my mouth. Even if a cortado sounds really good right now, actually , I can’t do it. I can make an active choice to get a perfectly fine drip coffee and get this line moving and get all these strangers-yet-neighbors their coffees too, or I can cause a big ol’ hitch in the giddyup. I hope I’m not trying to grandstand how perfect I am. I’m showcasing one part of how my brain works. I really don’t like inconvinencing other people. I notice, because it seems like plenty of other people don’t. People order cappaccinos and flat whites and all that shit without abandon. The line takes forever. It just is what it is. And we come to why I titled this The Rude Trifecta. These mocha-ordering fellow humans must fall into one of these categories: I actually don’t know how it would break down if there was a way to figure it out, but I suspect it’s a fairly even mixture. Like for some, it just doesn’t cross their mind that it’s any problem at all to order a 3 minute drink. It’s a coffeeshop and they ordered a coffee. Maybe if they thought about it for far too long like myself, they could see the problem, but that’s not their normal thinking pattern. For others, they couldn’t give any less fucks. Again it’s a coffeeshop and they ordered a coffee. They stood in line like everyone else. Yeah, it might take a while, but it’s their turn and they are going to use it. Put whip cream on it motherfucker. The last one is very similar to the above, but it’s more intellectual. Again it’s a coffeeshop and they ordered a coffee. This is not a rude action. It’s not on them to dechiper what is and isn’t rude on a menu , or to personally shoulder a understaffing issue. They might go so far as to think it’s actually rude in the other direction , where self-censoring an order doesn’t give the business the appropriate feedback on their operations. That’s why if I was with a friend and they did it , I’d be totally fine with it. I can’t do it. I can’t ask them to get me the americano. But their actions are their own and this isn’t a situation where I cast any judgement. I mean assuming it’s #3 and not #2, that is. Speaking of airports and flying, this is why I literally cannot recline my seat if someone is behind me. It takes up their room. Can’t do it. Reminds me of a recent-ish Marcel post : I feel like the neighbor: They don’t know that it’s rude They don’t care that it’s rude They disagree that it’s rude doesn’t know it’s rude doesn’t care it’s rude disagress that it’s rude

0 views

Visualizing Nix closures

tl;dr seenix.dev lays every byte of a Nix closure out on a map, one pixel per byte, and lets you zoom from a whole NixOS system down to the hex of . Try hello , firefox or a GNOME desktop . Nothing runs on a server. With the advent of LLMs I keep tugging at any crazy question I ask myself. I know there is the anti-AI crowd and they will happily proclaim anything pursued in this vein as “slop” but I am feeling fortunate to be able to explore these questions. My recent itch was to ask “what does a Nix closure look like?” and to answer it in a way that is interactive and visual . I wanted to see the bytes, not just the store paths. I had come across binvis.io on Hacker News and I found it a compelling way to look at data. I personally never found a need for it, but I found it fascinating none-the-less. 1 The timing for this itch was perfect. I noticed a trending thread on X where a Python binary seemingly includes and . 🤷 I built that tool. You can check it out at seenix.dev . It is a single-page web app that runs entirely in your browser, with no server. It fetches the narinfos of a closure and lays them out on a map, one pixel per byte, and lets you zoom in to see the bytes themselves. We can visualize the closure of that binary, , and see if it really does include those two packages. Turns out it does not. The closure is 41 store paths and 234 MiB, with no and no among them. Turns out those dependencies are build-time and are not included in the final runtime closure. We can visualize much larger closures. Here is a GNOME desktop: 1,324 store paths and 5.3 GiB, each colour one package. That picture needed zero NAR downloads. It was laid out in 3 ms from the narinfos alone. 🤯 The “trick” I learned to make this visualization possible, is the Hilbert curve . A Hilbert curve is a single, unbroken line that folds back and forth such that it completely fills up a flat square. It is a fractal . Every store path in the closure is sorted by name (the root first) and their NARs are concatenated into one long line of bytes. The Hilbert curve folds that line into a square, so byte n is pixel n along the curve. The Hilbert curve has two properties that lend itself nicely to visualize binaries and as a result Nix closures: Bytes that are near each other in a file stay near each other on the map. A NAR is a single contiguous range of bytes, so a store path is a single contiguous region on the map. A file inside that store path is a smaller contiguous region, and a section inside that file is smaller still and so forth. Squares are just byte ranges. Here’s a tiny 4×4 map. Each number is the byte that lands on that pixel: That means we can easily place a store path on the map by knowing its starting byte and its size. That’s what makes the map cheap to draw. 2 The layout only needs each path’s , which every narinfo carries, so the whole map exists before a single NAR is downloaded. Hovering already tells you which store path you are pointing at, its size, its retained size (the bytes that would leave the closure without it) and a “why is this here” chain back to the root. As you zoom in, the NARs on screen are fetched from the cache and the color fills in. Here is ’s closure, most of which is glibc: Blue is printable ASCII, red is high bytes, green is control bytes and black is . The speckled top is machine code. The big solid blue area at the bottom is glibc’s locale data, which is plain text. Keep zooming and every pixel becomes a byte you can read. Hovering names the file inside the NAR, and for ELF files, the section. That is of , in your browser tab, fetched from cache.nixos.org , without any server . 😈 Does everything need a purpose? Sometimes something is fun to make and to use with no real purpose. For fun, I even added a Save PNG button, and it saves the view at the canvas’s full resolution. The ultimate ricing of your NixOS system: a pixel image of your desktop closure. Can Omarchy do that? 😎 Anything you can export works: Drop the file and see the map. You can provide additional Nix binary caches to fetch NARs from as well. The source is at github.com/fzakaria/seenix . Go look at something big. Build without purpose. Have fun. Aldo Cortesi’s writing on visualising binaries is a great resource on this.  ↩ This is why the world is always a power of four bytes. hello’s closure is 36 MiB, which fills a bit over half of a 64 MiB square, and the rest is drawn as background.  ↩ Aldo Cortesi’s writing on visualising binaries is a great resource on this.  ↩ This is why the world is always a power of four bytes. hello’s closure is 36 MiB, which fills a bit over half of a 64 MiB square, and the rest is drawn as background.  ↩

0 views
Unsung Yesterday

Not everything needs to be a round rect

For the many early years of its existence, Chrome sported a pretty distinctive – perhaps even iconic? – look to its tabs… = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/not-everything-needs-to-be-a-round-rect/1.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/not-everything-needs-to-be-a-round-rect/1.1600w.avif" type="image/avif"> …with even the “new tab” button looking like a tab embryo waiting to be brought into existence. At some point, however, during one of the redesigns, the tabs have been flattened to look like many other round rects in the UI, and the new tab button asked to dress in the minimalistic button uniform every other button was already wearing: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/not-everything-needs-to-be-a-round-rect/2.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/not-everything-needs-to-be-a-round-rect/2.1600w.avif" type="image/avif"> Here’s a new example of this trend. iOS’s memorable tooth-shaped keyboard key extensions, there with us since 2007… that is, until yesterday, when iOS 27 designers turned them into Yet Another Round Rect: = 3x)" srcset="https://unsung.aresluna.org/_media/not-everything-needs-to-be-a-round-rect/3-framed.1600w.avif" type="image/avif"> = 3x)" srcset="https://unsung.aresluna.org/_media/not-everything-needs-to-be-a-round-rect/4-framed.1600w.avif" type="image/avif"> There would be a time in my life where I’d see these two as a triumph of minimalism and consistency. But I feel differently today. I don’t even mean that tabs should look a certain way to help users, or that skeuomorphism absolutely needs to come back, or that someone has to brush up on shape coding . I mostly feel that way because modern interface design practice – these ubiquitous round rects on ever-present white backgrounds, set in one of the near-identical neogrotesque fonts – is just so… boring. It’s not fun, not inspiring, not – in any real way – exciting. I also have this feeling that “consistency” might be just an excuse. Defaulting to round rects could be running away from a challenge; the original shapes would be harder to make work, but it was absolutely possible to do that, given enough effort and care. Occasionally a designer is faced with an important question that awaits an honest answer: are you doing something to make your user’s life simpler, or yours? It’s not that the first answer is always better than the second, of course; sometimes you have to put on your mask before helping others. But, without knowing all the considerations, I feel that way about these two examples – and a tinge of sadness seeing those unique shapes bulldozed. (And yeah, I know I’m not doing the case any favours by comparing these to teeth. I think originally the key shape might have been typewriter-inspired; early iPhone’s keyboards were making what to me felt like typewriter-esque sounds too.)

0 views
Stratechery Yesterday

OpenAI Ads, Amazon Ads in ChatGPT, Walmart to Accept Apple Pay

ChatGPT ads are working, and solve Amazon's biggest problem with chatbots. Then, Walmart finally gives in to Apple Pay, because fighting the status quo is hard.

0 views
neilzone Yesterday

Initial thoughts on the Social Media Platforms (Ofcom Licensing) Bill

There’s nothing like waking up to find people telling me about proposed new legislation which, if passed, would geoblock people in the UK from so many online services, end numerous services in the UK, and criminalise myriad people in the UK. Today’s proposal is the Social Media Platforms (Ofcom Licensing) Bill . The gist of the proposal is that anyone who “operate[s] a social media platform that is available to users in the United Kingdom” commits a criminal offence unless they obtain a licence from Ofcom, and comply with the terms of that licence. Is it a private members bill, and is unlikely to pass - more a declaration of intent than a serious attempt at legislating - so there is a risk that, in responding to it as a serious proposal, one gives it more credibility than it deserves. Nevertheless, here are three quick, pre-breakfast, thoughts, based on the text of the bill here . My starting point, in anything like this, is “what is the problem that the legislation is trying to solve?”. Here, I just do not know. I cannot get to the point of trying to assess whether it is the best way of trying to solve the problem (although this is incredibly unlikely), because I cannot tell what the problem is. The Online Safety Act 2023 already started down the very slippery slope of regulating people’s conversations, through the guise of requiring platforms to do things in respect of those conversation / interactions. Ostensibly it is not content regulation yet, in practice, that is really the outcome that is sought. The same is true here, and this bill is even more concerning. I cannot imagine someone attempting to pass a law telling pub landlords or cafe owners that they - on pain of criminal liability - : must take all reasonable and proportionate steps to ensure— (All I have done here is replace “content made available on its social media platform”, from clause 4 of the bill, with “conversation in the pub/cafe”, and “content” with “conversation” in (f).) I don’t know how someone might go about some of these things? How does the provider of, say, a running forum make a determination of whether a conversation contains misleading information? Is a campaign against facial recognition cameras in public places “harmful … to the public interest”? Who decides? How does a forum for vulnerable people who wish to share sensitive information comply with (e), to provide “transparent information concerning the identity and authenticity” of other users, without causing users harm and stifling their speech? How does this interplay with a user’s rights to freedom of expression, privacy, or data protection? The lack of a conjunction at the end of clause 3(a) renders the scope unclear. Does a platform have to meet both (a) and (b) to be in scope? Or either (a) or (b)? If it is an “or”, then the scope is very broad indeed. If it is an “and”, then it is slightly more narrow, but still incredibly broad. I do not know what “other than those with whom they communicate privately” is trying to get at. Does it include only direct messaging between a small number of participants? Is a large, but closed, group chat “private”? If I run a fedi service for my family, but everyone can see each others’ posts, is that private communication? There is no carve-out for small, low risk, services. Off the top of my head, I’d have to obtain a licence for several services that I run at home. This is an existing problem with the Online Safety Act 2023, but since the impact of this bill would be to criminalise me unless I obtained (and presumably paid for? since Ofcom could not run the infrastructure needed to staff etc. this for free) a licence. Right. Breakfast time. Oh my. that conversation in the pub/cafe complies with the laws of the United Kingdom; that conversation in the pub/cafe is not materially harmful to users or to the public interest; that conversation in the pub/cafe does not incite criminal conduct, violence, hatred or public disorder; that systems are in place to minimise the dissemination of materially false or misleading information; that users are provided with transparent information concerning the identity and authenticity of persons having conversations in the pub/cafe; that harmful conversation identified by Ofcom is removed, restricted or otherwise addressed within such period as Ofcom may specify.

0 views

Tag index for Org mode blog

Since people keep asking how this blog is made, and I don’t want to share the awful, terrible code that it is taped together with, I’ve decided to start explaining parts of it piecewise. Generally, any time something breaks and I have to fix it, I write down what I did and what it connects to. The most recent issue was the stack limit being blown by a helper function involved in generating the tag index. I had written it to be explicitly recursive, which worked fine with a small-ish number of published articles, but not anymore. The tag index creation follows a similar pattern to the RSS feed generation detailed in the previous article. (Continue reading the full article on the web.)

0 views
マリウス 2 days ago

llama.cpp with SYCL (oneAPI) for Intel Panther Lake on Gentoo

Alright, so if you’ve read the title and thought you were having a stroke, you might not be the target audience for this post. If, however, you thought “more tokens/s?” , you should keep on reading. If you remember my review of the new Lenovo X1 Carbon Gen 14 Aura with Intel Panther Lake Core Ultra X7 368H vPro from a while ago, you might recall that I had tested its local “AI” performance using Ollama, via Vulkan, which obviously didn’t perform particularly well across various models. The proper way to run local LLMs is to use Intel’s official oneAPI framework and compile llama.cpp with SYCL enabled, which will greatly increase performance. This post is a very brief write-up of how that can be done and primarily serves as yet another documentation for future-me. I’m assuming that you’re already running your Gentoo system with the driver and . First, install the necessary dependencies: You’ll likely have to unmask a couple of those packages, like , but that’s fine. When this is done, add your user to the and groups if you haven’t already and log back in. Test that the GPU is being recognized by : Next, go and get the “Intel Deep Learning Essentials” package and use to install it as a user. I chose the installation path , but you’re free to install it anywhere you please. Note: Even if you deselect the telemetry option, the installer will try to contact Intel’s servers post-installation, so make sure your OpenSnitch blocks all requests from that process towards the interwebs. While the FireBurn overlay has , it does not yet allow you to specify as a USE flag, and the official Gentoo repository has no llama.cpp ebuild at all. Hence we clone the project’s Git repository and compile it manually: Once llama.cpp finishes building, you can start it and have it download a model, e.g., Mistral or Qwen3: Open a browser at http://127.0.0.1:8080 , and you can try the model right away. On my Lenovo, I managed to increase the tokens/second by roughly 60% compared to what Ollama (via Vulkan) was able to achieve. For example, the Mistral model went from approximately 13.88 tokens/s to 22.36 tokens/s.

0 views
Andy Bell 2 days ago

Removing algorithmic feed slop from my life

Recently, during a very relaxing holiday in Cyprus, I had a revelation of sorts: my brain was rotting. I know this because I read a lot of books on holiday. It’s the only time I do read books! Well, I did read a lot on holiday, but the last couple of years I really struggled to focus. I used to put it down to “I’m just super tired, man, I don’t have the mental strength to concentrate” but that was probably only a part of it. I struggle to concentrate all the time — brains, innit — but it looks like algorithmic feeds of content, such as Instagram, TikTok and Bluesky (For You feed) have been doing a lot more damage to that existing problem than I thought. I decided to cut loose. I deleted Instagram, TikTok and Bluesky from my phone because I don’t use anything else. The feeling I had, for the first couple of days after that was the exact same feeling I had in attempts to quit nicotine, which was rather concerning. I persisted though, leaning more and more into my beloved RSS feeds whenever I had a pang to pick up my phone and scroll. I get my Bluesky content via RSS too, via a little system I’ve been tinkering with. I don’t like to miss what my pals are up to — especially where there’s an opportunity to signal boost — but I also think there’s value in reading posts from my peers and others. This is serviced well by my RSS setup, so if you see me in your notifications tab, that’s why. When I Log On™ to Bluesky, all I see is this: No surprises here! I’m feeling a lot less ground down and my brain is working much better, only after a week. My phone always has battery too, which is handy. The thing that’s really surprised me is I’m sleeping so much better than I was. I’ve had a hard rule for years: my phone doesn’t come into the bedroom, but even then, my brain was clearly struggling to relax after watching TikTok for an hour or whatever. I’m going to keep going with this to see where it takes me. I’ll miss sending good videos to my pals in Instagram and discovering new music to listen to, but that’s about it. The algorithms on that service, as well as TikTok (since the Americans took over) are really quite shit when you think about it and if you think about it even more, the content you do actually end up seeing really doesn’t improve your life at all. It just fills time. I guess I’m in a pretty fortunate position. I don’t rely on these services for my income so I have the option to just leave. I feel really bad for those who do rely on these services because it must feel like a prison a lot of the time. I’m also fortunate that I’m not rotting my brain talking to LLMs all day either. I can see the immense damage that is having on everyone that is into this technology. If you’re thinking “not me, though”, I’m afraid to say that yes, you too. I see it every day in my peers and it’s rather distressing. Protecting your intelligence is, and will continue to be, the most important thing to do. That’s a topic for another day though! Try removing yourself from algorithmic feed slop. It’s only affected me in a positive way so far, so it’s certainly worth a shot if you’re also feeling like your brain is rotting too.

0 views
iDiallo 3 days ago

AI Forces You to Commit to Your Initial Belief

Have you ever watched any old hacker movie lately? The hacker can write at 180 WPM, never using the backspace or changing his mind ever. It’s like before they ever started, they already knew the last character they would type on the screen. But that’s not how it works in real life, at least not for me. The idea changes mid-sentence. That's probably how most of the code I write ends up. I start with an idea in my mind. I think it's brilliant, maybe it’s a way to restructure the code, an overall design, or a specific paradigm. But when I start writing it, something else pops up. Seeing it halfway down the page, I realize it's not quite what I was imagining, and I change direction. The same principle applies when I'm writing a blog post or a story. I feel like I have a perfect concept in my head, but the moment I start putting words down, things shift and I adjust accordingly. That's why the final text rarely matches the original vision. It’s as if writing itself is the process of refining the idea. This process is lost when you use AI. You don't write out the details of an idea as you go; instead, you just give it the gist of it. It generates an entire wall of text instantly, and now you're stuck reading through it while the tool tries to preserve that initial draft, or its own interpretation of it at least. You only discover flaws or attempt to pivot while reading the generated output. But pivoting inside a wall of text is much harder than making a 180-degree turn mid-sentence. If you spot a small part you dislike and try to remove it, it might break something 20 lines down, forcing you to step back and make sure everything still aligns. Using an LLM forces you to commit to your very first thought. When you write manually, every keystroke is a chance for the idea to evolve. When you use a large language model, you have to absorb the entire output all at once, which ends up being far more time-consuming.

0 views
Kev Quirk 3 days ago

2026-09-13 10:35: Nelly the pup absolutely loving life in the field! 🤣

Nelly the pup absolutely loving life in the field! 🤣 Thanks for reading this post via RSS. RSS is ace, and so are you. ❤️ You can reply to this post by email , or leave a comment .

0 views

Why I Still Believe in Mentorship

One of the best tips I ever got was from my table tennis coach. He watched me play and said, “You are leaning forward with the wrong foot!” I’d been playing for years and thought I had a good stance. I’m left-handed, and no one had told me that I should put my right foot forward. I had copied the stance of other players without noticing the problem. It was obvious once he pointed it out. But until then, I hadn’t thought to question it, and more practice hadn’t helped me notice. That single observation improved my game more than any amount of practice because it gave me better control over my forehand play. Note that I didn’t actively seek out advice on my stance. Someone had to watch me play, observe, and then point out the single most helpful thing that would set me on the right path. Mentorship in Software In software, we are often expected to struggle alone. We’re supposed to learn systems design, architecture, testing, and refactoring largely by sitting with the problems. The times when I grew the most as a programmer were through thoughtful code reviews, but they are rare. Besides, the reviewer sees the end result but not the (sometimes flawed) reasoning that produced it. Pair programming gets closer. But two equally inexperienced programmers can reinforce each other’s bad habits. An agent can control the keyboard instead of helping you reason through a problem. It may produce better code without making you much better at writing it. A session where a machine fixes everything is productive, but being productive and growing as a programmer are two different things entirely. This is part of why I still believe in mentorship, even now that an LLM can answer so many programming questions. Going beyond the superficial questions I run a one-on-one Rust mentorship program , so I have a personal stake in this. Most of my mentees come with a concrete question: Often, answering the concrete question is beside the point. We have to look at the thought process that led to it. Before deciding whether something should be generic, for example, it helps to know what is expected to vary and why. Otherwise, we can spend a long time discussing how to build an abstraction that might be unnecessary in the first place. A mentor has enough distance from the problem to question its framing and enough experience to recognize familiar patterns. They notice which details you leave out, which patterns you reach for, and which you ignore. You may sense that something is wrong without having the words to describe it. LLMs can question assumptions, too! But I’ve found they usually work best when you already know what to ask. The difficulty is that we often don’t know which of our assumptions need challenging. Knowing what to ask can matter more than knowing the answer, and it’s hard to ask about something you haven’t noticed. You can also use an LLM to examine a decision, or you can use it as an echo chamber to justify it. The second use may feel more rewarding in the moment, but it leaves you where you started. Mentoring in Other Fields Nobody is surprised when a professional athlete has a coach. Musicians keep taking lessons after decades of playing. We don’t take this as evidence that they haven’t learned the basics. Quite the contrary: it’s an indication that they are serious about improving and that they’ve outgrown generic advice. It requires introspection to recognize that you can benefit from someone who can observe you from the outside. And in some sense, you put yourself in a vulnerable position: the wrong mentor can set you back, while the right one can dramatically accelerate your growth. Experience doesn’t remove the need for mentoring in software either. You can become an experienced developer while still carrying counterproductive habits you’ve never examined. If those habits let you get the work done, you may have little reason to suspect them and the problem gets worse because bad habits get reinforced. I could play table tennis with the wrong stance, too, just worse. The people I mentor are already experienced software engineers. They are perfectly capable of using LLMs in their work. Many are in leading positions as staff or principal engineers. And yet, they’ve realized there’s something missing that’s holding them back. They feel like they’re not progressing as quickly as they could, and their companies haven’t provided the mentorship they need. Friction Is Part of the Learning Process Another problem is that software gives us very uneven feedback. A syntax error gets your attention immediately because the code won’t compile. But a poor architectural decision can compile, pass the tests, get approved in review, and become a major problem months later when you lack the time to make changes. One way to learn architecture is to make bad decisions and sit with their consequences. That’s how I learned! The trouble is that it takes years, and it’s certainly not motivating. A mentor can streamline that process by asking questions you may not think to ask yet: There is no single “correct” answer to these questions. The answers depend on the constraints and the stage of the project. A mentor should help you examine those constraints and point out risks you’ve missed, while leaving the decision to you. If someone else chooses the design, you lose the practice of choosing it yourself. You also still have to write the code and live with all the consequences. But you go in with a better understanding of what might go wrong. And if it does, you have someone to help you work out why. That is how you develop judgment you can use on the next project. It’s a bit like climbing with a partner. Does Everyone Need a Mentor? If you’re just starting out, a book, a course, or a friendly community might be better value. If you need an answer to an isolated question, documentation or an LLM is faster and cheaper. If mentorship were only about transferring information, it would be harder to make a case for it. Personal mentorship becomes valuable when the potential payoff is a multiple of the investment. That is typically the case in a professional setting, where focused mentorship can help you land a better job or switch to a different team with a more interesting project. That doesn’t mean personal mentorship is a guarantee of success. Our busy days leave little time for deliberate practice. We have to carve out time for learning, which is hard, but the alternative is stagnation. As with going to the gym, paying for help doesn’t spare you the exercise. One hour of conversation cannot make up for a week without practice. You have to try things between sessions if you want the next conversation to be different from the last. Easier access to answers hasn’t changed any of this. What I want from mentorship is the kind of help my coach gave me: someone paying enough attention to notice what I couldn’t see for myself. That’s what I aim to offer in my Rust mentorship , at least: we work through hard problems together, explore the design space, and make deliberate choices. If I do a good job, my mentee will eventually surpass me, and I’d be proud to help them get there. Would you make this generic? Is it okay to clone here? How can I handle this error? Is this abstraction really necessary? How would an error propagate across the system? How can we test this?

0 views
Evan Hahn 3 days ago

Anecdotally, programmers dislike "reduce"

In short: from my experience, people like and , but not . I use functions like and all the time. When I put that code up for review, my peers rarely complain. I get plenty of feedback about other decisions, but not about my use of and . I cannot say the same for . Often, when I’ve submitted a patch with inside, I get a comment like, “this part is hard to read.” And I see way less than , , , and so on. Anecdotally, I have come to believe that programmers don’t like as much. I don’t know why, but I have a few theories: I usually just change to something else and move on. Even though I prefer it, I don’t usually care much. But it’s a little social phenomenon I’ve observed, and I thought I’d document it. I’ve also noticed this less recently, possibly because code review is less thorough nowadays. Do you notice this? Do you like ? Please tell me . is harder to read. is less familiar. can have worse performance compared to other options. is less elegant in languages I use, like JavaScript, Python, and Swift. In my blissful stint as a Clojure developer, I did not get this feedback. I’m wrong, and I’m seeing a trend that’s not real.

0 views
Unsung 3 days ago

Nova’s menu wayfinding

From its earliest days , Macs established an interesting convention – whenever you press a keyboard shortcut to an action that’s somewhere in the app menu, the matching top menu label blinks quickly. Here, I am pressing ⌘A (Edit > Select All), followed by ⌘+ (Format > Font > Bigger), and then ⌘B (Format > Font > Bold): I believe this is meant to help you connect those things better. While you might not need a map to an action that you already know a shortcut for, it might be helpful to tell you where to find other actions like it. I imagine it also helps whenever you press a wrong shortcut – or the right shortcut under the wrong circumstances – and you want to deduce what happened or what was meant to happen. Knowing roughly where a command “lives” makes it easier to open the menu and look for it, even if you might still have to dig through all the submenus. Recently, I spotted the programming editor Nova use a parallel technique. In its command palette, commands show their keyboard shortcuts, but if they’re hiding in submenus – also their menu path: = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/novas-menu-wayfinding/2.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/novas-menu-wayfinding/2.1600w.avif" type="image/avif"> = 2x) and (width >= 700px)" srcset="https://unsung.aresluna.org/_media/novas-menu-wayfinding/3.2096w.avif" type="image/avif"> = 3x) or (width >= 700px)" srcset="https://unsung.aresluna.org/_media/novas-menu-wayfinding/3.1600w.avif" type="image/avif"> I am not sure how effective either of these techniques is, and both can feel a bit… busy. But they also appear thoughtful – we’re all spatial creatures, and I imagine helping you visualize a map of the entire system of commands can make it easier for you to feel at home.

0 views
Gabe Mays 4 days ago

Making math automatic with Mathy

I finally built Mathy, a little project I’ve been thinking about for a couple years. It’s free, and no account is required if you want to try it out. I use Math Academy daily, which is the best way to learn math. But I wanted a mobile-friendly way to drill math and weak areas on the go that was more convenient/less cumbersome than Anki cards. It’s also a nice way to do something…

0 views
David Bushell 4 days ago

RSS Club #009: Domains

This is an RSS-only post, thank you for subscribing :) I keep buying domains! When will I ever learn? Domain prices are increasing (again) because why wouldn’t they? Sub-domains are free! Host your side project on a sub-domain! Then if you get bored, who cares? I used to know this but somewhere along the way I lost the knowledge. My old projects like Shiro and Tales and print.css live long happy lives undisturbed by man. I’ve been busy moving newer side projects to sub-domains or archiving them on my self-hosted Git forge . These are the ones I remember: expires in six months. Attic is archived. expires in a year. Croissant is now on a sub-domain. might needs to be renewed indefinitely to avoid squatters (it has juice). Is the strategy to stop redirecting and leave it blank for a year? Pikaday is now on a sub-domain. is impossible to move without severing RSS subscriptions. I thought a legacy framework like React would have died out by now; how wrong I was! is a piece of art waiting on Google to reap before it can expire. has a story behind it but I think interest has waned. Recent events transpired to nothing. The courtroom drama is glacial. turned into my second notes blog but the domain still lives. Cotton Coder was to be my brand for web education. Glad I didn’t invest there — yikes that side of the industry evaporated quickly! is expiring in 19 days, used once for a joke. I’ve lost track of how many domains I’ve bought for a year and never used. “Buying” is the wrong terminology. “Renting” or “leasing” should be legally enforced. I have a dozen dead domains I bought rented for possible company names before I settled on Valley Fold ( ). For brand names it’s difficult not to buy all the variations. I don’t own the but I did snap up the — primary, — criminal they sell that separately, and for future use like website staging. Want to guess how many TLDs and variations of I own (or owned) over the years? I let most go. It’s silly and expensive squatting them. FYI both myself and my ownership of pre-date the D-Bus project . Maybe it’s hell maybe it’s not, that’s not my beef! In hindsight though I wouldn’t have chosen if I knew it would become my freelance business. It took a year of indecisiveness before Valley Fold Ltd was born. I was kicking around other names and domains but I’m happy where I settled. I paid a three-figure transfer fee for on a whim (ouch). I quite liked the sound of it… no one else agreed. Originally I wanted (note the different spelling) but more than one “Quiet” agency exists with the same idea. Another business name candidate was “Focus Visible” inspired by Clearleft . A reference to CSS if you’re not familiar. I did like the nod towards accessibility too. The problem with this name is: do I use or with a hyphen? The hyphen fits the CSS property but hyphenated domains are lame. I bought both. All the big TLDs in fact. I was surprised they were available (not anymore lol). “Focus Visible” also sounds like a team name from The Apprentice . Which is cringe. Needless to say I have more domains waiting to expire than I’m actively using. I set up a second registrar account with notifications disabled so that I don’t have to see them! My new goal is to reach 2027 without renting a new domain. We are not in a golden age of web development and frugality is necessary to survive! Thanks for reading! Follow me on Mastodon and Bluesky . Subscribe to my Blog and Notes or Combined feeds.

0 views
Lalit Maganti 4 days ago

I made a build visualizer to understand Bun’s compile times

I built buildprof ( Github ), an open-source tracing tool that shows where the time goes when you compile software on Linux. Here’s a realtime video of it profiling a clean build of ripgrep: Watch the buildprof demo Sometimes, builds are slow because there is simply a lot of code to compile. But more often than not, there are fixable problems: poor parallelism, repeated work, dependency downloads or a huge compiler/linker invocation. buildprof makes all of this clearly visible, so you can see what’s worth investigating and optimizing. You run it by putting in front of any build command you already use: buildprof records every process your build command launches, including their subprocesses (and their subprocesses…), and lays them out on one timeline. Time moves from left to right, bar width shows duration, and child processes appear beneath whatever launched them. I made buildprof because this tweet from Jarred Sumner, chief architect of the Bun JavaScript runtime, was living rent free in my head: Specifically, the claim that Bun’s new Rust build was >5× faster on Linux than its old Zig build really bothered me. In my experience, Zig projects had usually compiled much faster than Rust projects of similar complexity. That intuition was enough to make me feel there was a mystery to solve. This was further compounded by another important, yet easily missed, detail in the tweet: the Zig build used Full LTO, while the Rust build used ThinLTO. Compilers normally optimize separate compilation units largely in isolation. 1 Link-time optimization (LTO) lets them optimize across those boundaries. Full LTO brings those units together into one large optimization job, while ThinLTO preserves more separation so much of the work can run in parallel. From past experience, this difference can have an enormous effect on build time. The tweet mentioned it in passing, but I wondered how much of the headline improvement it explained. I started by trying to reproduce the numbers. I checked out Bun 1.3.14 and Bun 1.4.0 and wrote some scripts to replay their Linux x64 CI builds on a 6-core, 12-thread Linux VM. The scripts preserved the build steps and their dependencies, running everything on one machine. 2 My timings were in the same ballpark as Jarred’s: OK, so the gap showed up on my machine too. But a lot had changed between the two measurements besides the language; so what was actually responsible? Was it the Zig compiler that was taking all that extra time? Or maybe it was the Full LTO link? Or perhaps there was something else in Bun’s build I hadn’t even thought to look at? This is where my profiling and developer-tools brain kicked in. Usually, when I’m trying to understand why something is slow, I want a trace: what happened, when it happened and how long it took. It would be really cool to have that for these builds, to put them on a timeline and see where their time actually went. But a build involves a lot of different tools, each with its own idea of what’s happening. What could I record that would let me see across all of them? When you type or , it feels like you are running one program. The build system works out what needs to be rebuilt, the ordering between those pieces and what can run in parallel. But generally, it does not perform all that work itself; it launches compilers, code generators, archivers, linkers and arbitrary scripts. Which can launch more programs which launch some more… Different build systems describe that work in different ways. Cargo sees crates, Ninja sees build edges and CMake generates instructions for another build system. From the operating system’s point of view, however, they (mostly) look like processes launching other processes. 3 A Rust build, for example, might contain a chain like this: If we record when each subprocess starts and ends, we can lay them out on a timeline. Here’s what that chain looks like in buildprof: There are also several nice properties to visualizing a build at this layer: This gave me a starting point for buildprof: record the process tree, then turn it into a timeline I could explore. There are plenty more details to get into, which I will do later. But once I had that working, I could finally go back to my initial question: what was Bun doing for those twenty-four minutes? I started by recording the Zig-era CI build with buildprof, using the same scripts as before : Explore in buildprof Right away we can see a huge problem: the linker invocation dominates the build time. It ran alone at the very end for over sixteen minutes, about two-thirds of the entire build. What the heck was it doing for all that time? Clicking on the linker shows its command line, which buildprof captures automatically: There’s Full LTO, just as Jarred said. Given how long the link was taking, it was now my main suspect. But the process tree alone couldn’t tell me whether LTO was actually responsible for those sixteen minutes. Thankfully, LLD records its own internal timing events, and buildprof can include them when you use . I recorded the final link again , this time with enabled: Explore in buildprof Now we can see that LTO is where almost all the time goes. The linker is running compiler passes over the program, not just combining already-compiled files. The bar alone takes just over ten minutes and includes the passes which generate machine code. 4 With so much of the Zig build spent in LTO, I wanted to see how much time the Rust build spent linking. I recorded that build too: Explore in buildprof Just 2m24s. And this time, as expected, the linker command contains : Both builds were doing LTO, but with different settings and very different link times. What if I kept Bun’s Zig code and changed Full LTO to ThinLTO? How much of the gap would that close? I switched Zig Bun’s build flags to ThinLTO and recorded another clean build, along with a fresh Full-LTO build for comparison: Explore in buildprof: Full LTO · partial ThinLTO The link got 3m40s faster in this pair of recordings, but it was still taking nearly thirteen minutes. Why was linking still so expensive? Looking back at the compiler trace, a lot of the work was on functions with in their names. That’s JavaScriptCore, the engine Bun uses to execute JavaScript. The linker was spending time compiling the JavaScript engine too. 5 Clicking on the linker invocation showed the WebKit libraries among its inputs, including : Following those inputs back through the build, I found that Bun wasn’t compiling these libraries itself. It was downloading them from a separate WebKit build. And when I checked that build’s flags , there it was again: . The Rust build used a newer WebKit revision whose build recipe selected ThinLTO . Even though I had changed how Bun compiled its own code, those downloaded libraries still contained Full-LTO inputs and so the linker still had to optimize that code and turn it into machine code. To change that, I would have to rebuild WebKit too. I checked out the historical WebKit revision and rebuilt it and its ICU dependencies with compatible ThinLTO settings. Then I replaced the downloaded libraries with the ones I had built, keeping the ThinLTO changes to Bun. Here are the recorded builds: 6 The link now took 7m22s. Still slower than the Rust build, but enough of an improvement that I wanted to look beyond the linker. The build still took fifteen minutes, and nearly eight of those passed before the linker even started. What was it waiting for? I went back to the original CI trace to follow the inputs from Bun’s own code. buildprof also records which files each process reads and writes. If a process reads a file another wrote, it links the two together under the hood. Turning on “Show on timeline” draws those links as arrows. Here, the linker reads from the C++ compilation and from Zig. Both arrive through copy steps; following those back takes us to the processes which produced them: The C++ side of the compilation finished first. The linker was waiting for , so it could not begin until the Zig branch had finished too. It was at this point I went back to the Rust build and compared against how it worked, and the main reason the Rust build was faster became obvious: Bun has been split into >90 crates, while in Zig it was all trying to compile as a single Zig module! This meant that the Zig build cannot parallelise the same way Rust can. I also suspect, though I did not prove this, that it explains the slow linking: the linker has to optimize one huge ThinLTO bitcode module instead of the same work spread across crates. It was at this point I had to stop: to go any further, I would have to split up the Zig module myself, and given that this code is all obsolete anyway, I didn’t think it was worth doing that. Summarizing: And fwiw, the traces had also turned up a few things I couldn’t resist poking at… In the middle of Bun’s CI build, I found commands asking the public internet for the machine’s IP address, inspecting running Docker containers and reading the latest Git commit message. These take well under a second altogether. Nothing to optimize but I just wasn’t expecting to find them in a build trace. The builds above reused downloaded dependencies, so I also recorded a fresh WebKit fetch . Downloading and extracting the archive took about twenty seconds. For the first twelve, all we see is Node running. Then it launches and , and we can see the extraction separately. Earlier, we followed the linker’s inputs back to Bun’s C++ compilation. We can look inside those compiler invocations too. I picked one of the last files to finish, , and replayed its Ninja command with . For Clang, buildprof enables and adds its internal timings to the process timeline. 7 The replay took about twelve seconds, split almost evenly between Clang’s frontend and backend. Zooming in further, we see , one of the phases of Clang, accounts for over four seconds of the backend’s work. The recording side of buildprof uses , the same Linux interface used by debuggers. I did consider both eBPF and ftrace, but is just straight up perfect for exactly this type of problem; eBPF tracing means and permissions and hooking into potentially unstable tracepoints/kernel functions. While with ftrace, I’d have to juggle tracing instances to avoid interfering with other users, and getting the filters perfect for just the build process and all its descendants is cumbersome. 8 With , I can launch the build and follow its children directly. Its built-in events tell buildprof when processes fork, exec a new program or exit. And for filesystem activity, buildprof uses a seccomp filter to intercept only the calls it needs. How much buildprof costs is almost entirely down to how many files the build opens. For ripgrep, recording barely changed the build time. Redis opened files much more often, and recording added about five seconds: 9 If that overhead gets in the way, you can turn off filesystem tracing with and keep the process timeline. I work on Perfetto , so it was a natural starting point for the UI; buildprof’s UI is a soft fork of the Perfetto UI. I could have just opened the recordings on ui.perfetto.dev , but I wanted control over how the process tree was laid out, which details appeared when you clicked a command, and things like those on-demand arrows between file producers and consumers. Fortunately, we’ve spent the last several years working on making the Perfetto UI extensible through plugins . Most of buildprof’s UI is reusing that infrastructure. Perfetto handles the hard stuff (parsing traces, querying events, rendering the timeline and managing workspaces) and I get to focus on what makes those things useful for builds. I plan on going into a lot more detail about the recorder and UI in a separate technical post. Subscribe if you’d like to be notified when it comes out! :) These days it’s very easy to make a tool just because you can. But that wasn’t the case here; before building buildprof, I looked long and hard for an existing tool that could give me this view. I started with ninjatracing , which I’ve used many times. It turns Ninja’s build log into a timeline showing what ran and how much ran in parallel. Here’s the Ninja log from the Zig-era build . But Ninja only sees part of Bun’s build. The scripts which invoke it are missing from its log, and commands it runs appear as single blocks even when they launch whole trees of subprocesses. There were several other tools, each covering different parts of the problem: What the Fork ( via ) came closest: it follows processes across build systems and presents a build-specific view. But as far as I could tell, it still appears to be in private beta and there don’t seem to be any plans to make it open source. buildprof already does what I wanted it to do, and I plan to keep working on it as I use it on my own builds. But there are a few things I’d like to improve. Recording overhead is one; the Redis measurements showed there’s room to improve filesystem tracing, especially for builds which open lots of files. I’d also like to support macOS where I do some of my work and maybe Windows if there’s interest. There are also more build systems and toolchains I’d like to test, including npm, Gradle and Bazel. Computing critical paths would also be a big improvement: we followed dependencies by hand in this post, but buildprof could help identify the chain of work holding up the build and automatically annotate it. I’ll probably tackle these as and when I need them. But if you try buildprof and there’s something you wish it could do, I’d be interested to hear about it . What people find useful will help me decide where to spend more time. I managed to satiate my curiosity, though I ended up spending rather more time on this than I expected. Along the way I built a tool I now want to have around whenever a build is taking too long. I know I’ll come back to buildprof the next time a slow build annoys me. If you have one of those builds too, give it a try . I’d love to hear what you find! In C and C++, a compilation unit is usually a source file together with its included headers . Rust compiles crates , which can be split into multiple code-generation units . Zig normally compiles a program’s Zig sources together as a single compilation unit . Bun’s Zig compiler fork supports splitting that into multiple LLVM modules, but its CI build explicitly selected one when LTO was enabled .  ↩︎ The Zig-era CI build ran its C++ and Zig compilation stages on separate Buildkite machines and passed their outputs to a final linking stage. My script ran those stages concurrently on one machine, waited for both outputs, copied them locally instead of transferring them over the network, then linked them. This should preserve the dependency graph, but due to the hardware differences and running both stages on one machine, resource contention would obviously be quite different. Also note that my timings are individual runs (albeit ones which were quite stable) while Bun’s reported figures are medians.  ↩︎ A process can do substantial work internally, including running multiple threads, without launching anything else. The process timeline won’t show that parallelism. To see inside a process, we need tracing from the program itself, as Clang and LLD provide in the examples below.  ↩︎ LLVM emits from its legacy pass manager, which LLD uses for code generation. The inlining and other IR optimization passes can appear before it, so this bar is not the total time spent optimizing a module.  ↩︎ In the earlier Full-LTO linker replay, 26,825 events with JSC symbols total about 209s. This is summed event time, not a measurement of JavaScriptCore’s entire contribution to the link. One example event takes 2.94s; its symbol demangles to .  ↩︎ Recording script . These timings are just for building Bun with the libraries already available; the WebKit and ICU rebuild happened beforehand and isn’t included. Of course, I could point buildprof at that build too, but that’s another rabbit hole… I did not rebuild a matching Full-LTO WebKit archive as a control, so I cannot attribute every second saved to the LTO setting alone.  ↩︎ buildprof currently supports compiler traces from Clang, LLD and nightly Rust.  ↩︎ eBPF tracing uses capabilities such as and , as described in the kernel’s capability definitions . ftrace provides separate tracing instances and PID filters , but these still need configuring and access to tracefs. also depends on the host’s security settings; containers may need additional permissions to allow tracing child processes.  ↩︎ Medians of five clean builds per mode on the same VM, with six build jobs. Measurement script .  ↩︎ It’s build-system agnostic : Cargo, Ninja, Zig, Make and most other build systems do much of their work by spawning processes, so we do not need to write a special integration for each one. It naturally includes custom scripts : This includes both scripts above the build system (repository setup, dependency fetching) and scripts underneath it (code generators, asset processors). We can follow the files between build steps : recording which files each process reads and writes lets us see which steps produce the inputs for others. This even works across build systems! The huge outlier in the initial Zig build vs the Rust build was the massive linker step which ran alone at the end of the build. Changing the LTO settings for just Bun was not sufficient as WebKit, a significant part of the build, still used Full LTO. Once I had done this, the Zig build dropped from twenty-four minutes to fifteen. Even after this, linking still took 7 minutes and the whole build 15 minutes. The overwhelming difference which remained was structural: Rust spreads compilation across >90 crates while the Zig build funnelled everything through a single module. Cargo timings works well for Cargo-managed builds, but cannot break down arbitrary work inside or see wrapper scripts above Cargo. In Bun, Cargo is only part of the build: the report I captured covered 1m51s of a 5m40s CI build. Clang’s gave us the detail inside a compiler invocation, but cannot show what the rest of the build is doing while Zig’s Tracy integration goes deeper still and is intended more for understanding the compiler itself. and can follow arbitrary processes through and , but show general process events rather than a build-oriented timeline. In C and C++, a compilation unit is usually a source file together with its included headers . Rust compiles crates , which can be split into multiple code-generation units . Zig normally compiles a program’s Zig sources together as a single compilation unit . Bun’s Zig compiler fork supports splitting that into multiple LLVM modules, but its CI build explicitly selected one when LTO was enabled .  ↩︎ The Zig-era CI build ran its C++ and Zig compilation stages on separate Buildkite machines and passed their outputs to a final linking stage. My script ran those stages concurrently on one machine, waited for both outputs, copied them locally instead of transferring them over the network, then linked them. This should preserve the dependency graph, but due to the hardware differences and running both stages on one machine, resource contention would obviously be quite different. Also note that my timings are individual runs (albeit ones which were quite stable) while Bun’s reported figures are medians.  ↩︎ A process can do substantial work internally, including running multiple threads, without launching anything else. The process timeline won’t show that parallelism. To see inside a process, we need tracing from the program itself, as Clang and LLD provide in the examples below.  ↩︎ LLVM emits from its legacy pass manager, which LLD uses for code generation. The inlining and other IR optimization passes can appear before it, so this bar is not the total time spent optimizing a module.  ↩︎ In the earlier Full-LTO linker replay, 26,825 events with JSC symbols total about 209s. This is summed event time, not a measurement of JavaScriptCore’s entire contribution to the link. One example event takes 2.94s; its symbol demangles to .  ↩︎ Recording script . These timings are just for building Bun with the libraries already available; the WebKit and ICU rebuild happened beforehand and isn’t included. Of course, I could point buildprof at that build too, but that’s another rabbit hole… I did not rebuild a matching Full-LTO WebKit archive as a control, so I cannot attribute every second saved to the LTO setting alone.  ↩︎ buildprof currently supports compiler traces from Clang, LLD and nightly Rust.  ↩︎ eBPF tracing uses capabilities such as and , as described in the kernel’s capability definitions . ftrace provides separate tracing instances and PID filters , but these still need configuring and access to tracefs. also depends on the host’s security settings; containers may need additional permissions to allow tracing child processes.  ↩︎ Medians of five clean builds per mode on the same VM, with six build jobs. Measurement script .  ↩︎

0 views
Anton Zhiyanov 4 days ago

Solod 0.4: Better C interop

Solod is a subset of Go that translates to regular C — with zero runtime, manual memory management, and source-level interop. It's designed for two main audiences: The new Solod release provides an easy way to call third-party C libraries, makes a large part of the standard library freestanding, and impoves the tooling. Automatic bindings • Freestanding packages • Type assertions • C interop • Multi-package testing • Checks and targets • Windows • Wrapping up Sobind generates bindings — stubs for calling third-party C libraries from Solod. It parses files and emits a Solod source file with necessary structs, unions, constants, variables, function pointer typedefs, and function declarations. You can then use the generated types and functions in regular Solod code: Usually, the generated bindings are good enough to use as they are, without any manual changes. I have also prepared bindings for popular C libraries like libuv , raylib , sodium , and sqlite . Unlike Go, calling C from Solod has zero overhead — Solod code is just regular C in the end. At some point I decided to make as many packages as possible freestanding — independent of any libc implementation or specific OS runtime. That went pretty well. Solod now has 37 standard library packages, and 31 of them work in freestanding mode. These packages work in freestanding mode with no restrictions: These packages work in freestanding mode with certain limitations: There's a separate post with more details if you're interested. A comma-ok type assertion is now fully supported for non-empty interfaces: Which translates to the following C code: Previously, the only two supported forms were a direct assertion like and a check-only form like . The package now supports more common C types: There's also a type, which maps to a C . You can use it where C expects a pointer: Finally, there are some useful cast functions. reads the bits of a value as another type of the same size: You can use instead of a pointer conversion such as . and return a typed pointer to the string or slice data: They replace and . can now run tests from multiple packages at once. If you use a pattern that ends with , it will select every package that has a subdirectory under its base directory: The entire run only needs one translation, one compilation, and one execution, which is much faster than running it separately for each package. The flag restricts the run to only the packages listed in a file: , , and take two new flags: and . specifies the target platform for cross-compilation. Use the same value that and accept after : enables code analysis: The default optimization level is . You can use to change it. The standard library now builds for and . All packages in the freestanding set work. Packages that require POSIX ( , , , , , ) are not supported. You can use to cross-compile for Windows: Not the first-class Windows support that Go offers, but it's better than nothing. With v0.4, Solod can work with almost any C library thanks to automatic bindings. The freestanding-aware standard library makes the language a viable option for bare metal programming. Extra interop helpers make C-calling code easy to read, and better tooling keeps tests fast. There's still a lot to do, of course. In the next release, I plan to focus on the standard library and bring over some hashing and crypto packages from Go. More C library integrations are on the way too! If you're interested, take a look at Solod's readme — it has everything you need to get started. Or try Solod online without installing anything. Go developers who want low-level control without having to learn another language. C developers who like Go's style. depends on a user-provided hook to read random bytes. depends on a user-provided hook to print formatted text. offers a working subset of features. works fully, except it can't resolve an IPv6 zone name. works on targets that support lock-free instructions. depends on a user-provided hook to print test results. reads the clock using user-provided hooks. depends on hooks from both and .

0 views

In the world

In memory of Nevan Scott, 1985–2026 I met Nevan shortly after I began A Working Library . I don’t seem to have that particular email around anymore, but I recall that he wrote the politest, most generous note to alert me to a typo. I think it was late 2008, or maybe early 2009. We would end up crossing paths online many times in the following years, over Twitter—those were the good days, for some of us, at least—but also email, which then and now remains a more humane way of talking. I made a lot of typos; we talked about books and tech and work. We stayed in touch as our various work lives changed—he moved to Europe, I moved into (and eventually out of) startupland. At some point I needed some technical counsel with what I was only just beginning to realize was much more than a blog, and it was Nevan who I reached out to. There’s a real intimacy to letting someone in to your private repo, letting them see all the embarrassing first (and second, third, fifth) drafts, but I never doubted I could trust him. He was not only generous with his support, but eager to step in as a peer and collaborator. He took my work seriously—a great gift, too great to ever fully repay—but he took his own work seriously, too; everything he did, he did well and with care. For the last decade and then some he has been a regular contributor behind the scenes, offering astute feedback and proposing different ways to set things up, showing me how to do the things I could think of but didn’t know how to pull off. And of course, catching typos. We had a long-running barter agreement: he would reach out to talk about his work and I would listen, offer counsel, help him see what he already knew; I would reach out when I broke something, always with the caveat that it wasn’t urgent, to get to it when he had the time. And inevitably I’d get a pull request an hour or two later with a proposal, plus a bug fix or upgrade he noticed while he was working. It sounds incredibly mundane to talk about it now, a friendship measured in video calls and commit messages, but then the word mundane comes from the Latin mundi, or world. To be mundane is to belong to the world, to be in the world, of it and within it. Not outside of it—not lost, the way so much technology tries to make us lose ourselves—but in the world as only the truly living can be. As we can only be with each other. Nevan was a teacher. In all his work, in building schools and publications, he never lost his love for teaching. He was most himself, he told me, when he was in class, with his students. In the last year, he began teaching how to use AI in design, a topic on which he was intensely conflicted. He worried he was doing harm. But he also knew that the people who came to his classes were going to learn about AI one way or another, and maybe if he was the one teaching them, they would learn not only how to use AI but how it would try to use them ; that this was the kind of learning that should be undertaken only with the greatest of caution. Caution is, in Samuel Johnson’s definition, “provident care”—that care which foresees and considers the future. Our friendship covered a period of years in which the future would change dramatically: from an excitement and even optimism about tech (unearned and unwarranted, in retrospect), to a dehumanized nightmare, stultifying and terrifying in equal measure. Through it all we both maintained a sense that—whatever the horrors of capitalism—there was something keenly human and joyful about putting words and type on the internet. That we did so is a credit to our collaboration, and Nevan’s beautiful legacy. Near the end of The Blue Place , as Julia is dying, she tells Aud to “stay in the world.” Aud promises to do so, but the truth is she does not know how. It is so very hard to stay in the world, to hold on to the mundane and ordinary, those fragile human connections, amidst all the forces arrayed against us. I think it is only in the effort to learn that we come close to that knowledge; and perhaps only in the effort to teach that the learning really takes hold. View this post on the web , reply via email , or become a supporter .

0 views
Farid Zakaria 4 days ago

A Nix store is three functions

While building trynix I needed somewhere to host a store-path that did not exist on cache.nixos.org . 1 I wanted to demonstrate that non-Nixpkgs store paths could be booted just as easily. The only requirement seemed to be a lenient Cross-Origin Resource Sharing (CORS) policy, , because the fetch happens in JavaScript. Turns out that GitHub Pages sets that header on every file it serves. 😈 I committed the output of to my Git repository and voilà, I have a free Nix substituter. I seem to be late to the party on this discovery. tomberek’s github-store is a cache assembled out of GitHub release assets. 2 GitHub Pages or Releases are a static file server. It has no idea what Nix is. If a humble file server can be a Nix binary cache, what else could we use? Turns out that in order to be a Nix binary cache, you must implement only three simple functions. The Nix client does not care what medium you use to implement them although HTTP is the most common and included by default in CppNix 3 . Anything that can answer those three requests can be used as a remote Nix store . 4 The reason we can be this careless about transport is that Nix does not trust it. A narinfo’s signature ( ) field covers , , and . It does not cover , , or . Once the archive is fetched, Nix decompresses it and checks that the matches. This is the special sauce of how packages that were signed by cache.nixos.org can be fetched from any other binary cache as an intermediary, and the signature still validates. The field does not even have to be on the same host as the narinfo. It can be anywhere on the internet, and it can be a different protocol than HTTP. Nix does not care. The only thing that matters is that the archive fetched from has the same as the narinfo. For protocols that are not included by default in the Nix client, you can always write an HTTP proxy that translates the three functions to whatever medium you want. In research for this post, I found a few interesting ones. gachix : puts the archives in git’s object database. Git content-addresses and delta-compresses blobs already, so the store dedupes itself; the author reports roughly 82% smaller than the equivalent plain cache. DNS : I wrote a proof-of-concept that puts the narinfo and 4 KiB slices of the archive in TXT records. The narinfo is small enough to fit on one record but the archive needs to be chunked. pastebin : a pastebin can hold the narinfo and the archive. The narinfo is small enough to fit on one paste, but the archive needs to be chunked. Many pastebins have an expiry policy which acts as a natural garbage collector. nixcache-oci : uses an OCI registry to store Nix archives. infinite storage glitch : encodes data within a video and uploads it to YouTube. “Everything is available on npm” – Some person on the internet Unsurprisingly, npm is a great binary cache and it has some interesting properties for release management we can ab use. emits a directory and npm publishes directories: a match made in heaven. 💑 Let’s walk through a small example. It is dynamically linked against glibc, so the closure is five paths and roughly ~36 MiB: We copy it to a local cache, signed with our own key, and add the one file npm needs ( ): then dutifully packages our complete closure for us: is now a real package on the public npm registry. It is now a substituter you can point Nix at directly: Note We have to use to run the binary because is a chroot store and all the are still under . If we had relocatable binaries we could run it directly. That is Nix fetching the complete closure from npm and running it. 🤯 We can distribute Nix packages to non-Nix users, let the infection spread! As an added bonus, similar to Nixpkg and NixOS we can get nice “channel” semantics by using npm’s dist-tags. The tag is mutable and points to the latest version, while each version is immutable and points to a specific store path. The major downside of this approach is that npm ahs no incremental publishing. Every version is a whole tarball, so fifty closures sharing glibc upload glibc fifty times. We could fix that by publishing each store path as a separate package, and then having a small index package that points at them. Each store path would then be uploaded exactly once. I won’t build that though as it’s not in good faith to the npm ecosystem. What other store implementations can we find? I was also waiting for @domenkozar to enable CORS on cache.nixos.org so I could use it.  ↩ In order to be a Nix binary cache, the prefix is stripped from the field in the narinfo, because GitHub releases are a flat namespace.  ↩ You can write a Nix plugin to implement a new protocol if you wanted.  ↩ We will see that that they must not all be all on the same medium, protocol or domain even!  ↩ I was also waiting for @domenkozar to enable CORS on cache.nixos.org so I could use it.  ↩ In order to be a Nix binary cache, the prefix is stripped from the field in the narinfo, because GitHub releases are a flat namespace.  ↩ You can write a Nix plugin to implement a new protocol if you wanted.  ↩ We will see that that they must not all be all on the same medium, protocol or domain even!  ↩

0 views