frontpage.
newsnewestaskshowjobs

Made with ♥ by @iamnishanth

Open Source @Github

fp.

Decorative Cryptography

https://www.dlp.rip/decorative-cryptography
59•todsacerdoti•2h ago•13 comments

Databases in 2025: A Year in Review

https://www.cs.cmu.edu/~pavlo/blog/2026/01/2025-databases-retrospective.html
88•viveknathani_•3h ago•12 comments

A spider web unlike any seen before

https://www.nytimes.com/2025/11/08/science/biggest-spiderweb-sulfur-cave.html
72•juanplusjuan•3h ago•23 comments

Revisiting the original Roomba and its simple architecture

https://robotsinplainenglish.com/e/2025-12-27-roomba.html
21•ripe•2d ago•5 comments

Lessons from 14 years at Google

https://addyosmani.com/blog/21-lessons/
1258•cdrnsf•19h ago•539 comments

During Helene, I just wanted a plain text website

https://sparkbox.com/foundry/helene_and_mobile_web_performance
209•CqtGLRGcukpy•7h ago•115 comments

The unbearable joy of sitting alone in a café

https://candost.blog/the-unbearable-joy-of-sitting-alone-in-a-cafe/
626•mooreds•19h ago•371 comments

Show HN: Terminal UI for AWS

https://github.com/huseyinbabal/taws
317•huseyinbabal•14h ago•156 comments

Logos Language Guide: Compile English to Rust

https://logicaffeine.com/guide
39•tristenharr•3d ago•21 comments

Why does a least squares fit appear to have a bias when applied to simple data?

https://stats.stackexchange.com/questions/674129/why-does-a-linear-least-squares-fit-appear-to-ha...
245•azeemba•14h ago•66 comments

Street Fighter II, the World Warrier (2021)

https://fabiensanglard.net/sf2_warrier/
383•birdculture•19h ago•68 comments

Why Microsoft Store Discontinued Support for Office Apps

https://www.bgr.com/2027774/why-microsoft-store-discontinued-office-support/
28•itronitron•3d ago•27 comments

I charged $18k for a Static HTML Page (2019)

https://idiallo.com/blog/18000-dollars-static-web-page
293•caminanteblanco•2d ago•72 comments

Baffling purple honey found only in North Carolina

https://www.bbc.com/travel/article/20250417-the-baffling-purple-honey-found-only-in-north-carolina
80•rmason•4d ago•20 comments

Building a Rust-style static analyzer for C++ with AI

http://mpaxos.com/blog/rusty-cpp.html
57•shuaimu•5h ago•26 comments

Monads in C# (Part 2): Result

https://alexyorke.github.io/2025/09/13/monads-in-c-sharp-part-2-result/
24•polygot•3d ago•19 comments

Web development is fun again

https://ma.ttias.be/web-development-is-fun-again/
395•Mojah•19h ago•487 comments

Eurostar AI vulnerability: When a chatbot goes off the rails

https://www.pentestpartners.com/security-blog/eurostar-ai-vulnerability-when-a-chatbot-goes-off-t...
151•speckx•13h ago•37 comments

Show HN: Circuit Artist –Circuit simulator with propagation animation and rewind

https://github.com/lets-all-be-stupid-forever/circuit-artist
4•rafinha•4d ago•0 comments

Linear Address Spaces: Unsafe at any speed (2022)

https://queue.acm.org/detail.cfm?id=3534854
158•nithssh•5d ago•115 comments

Show HN: An interactive guide to how browsers work

https://howbrowserswork.com/
233•krasun•19h ago•33 comments

How to translate a ROM: The mysteries of the game cartridge [video]

https://www.youtube.com/watch?v=XDg73E1n5-g
18•zdw•5d ago•0 comments

Claude Code On-the-Go

https://granda.org/en/2026/01/02/claude-code-on-the-go/
325•todsacerdoti•14h ago•208 comments

Six Harmless Bugs Lead to Remote Code Execution

https://mehmetince.net/the-story-of-a-perfect-exploit-chain-six-bugs-that-looked-harmless-until-t...
65•ozirus•3d ago•17 comments

NeXTSTEP on Pa-RISC

https://www.openpa.net/nextstep_pa-risc.html
34•andsoitis•9h ago•7 comments

Ripple, a puzzle game about 2nd and 3rd order effects

https://ripplegame.app/
124•mooreds•16h ago•32 comments

Moiré Explorer

https://play.ertdfgcvb.xyz/#/src/demos/moire_explorer
167•Luc•21h ago•19 comments

Agentic Patterns

https://github.com/nibzard/awesome-agentic-patterns
125•PretzelFisch•15h ago•22 comments

Anti-aging injection regrows knee cartilage and prevents arthritis

https://scitechdaily.com/anti-aging-injection-regrows-knee-cartilage-and-prevents-arthritis/
319•nis0s•19h ago•120 comments

Bison return to Illinois' Kane County after 200 years

https://phys.org/news/2025-12-bison-illinois-kane-county-years.html
152•bikenaga•5d ago•46 comments
Open in hackernews

Was it a billion dollar mistake?

https://www.gingerbill.org/article/2026/01/02/was-it-really-a-billion-dollar-mistake/
53•signa11•23h ago

Comments

Kinrany•21h ago
> If you want to make pointers not have a nil state by default, this requires one of two possibilities: requiring the programmer to test every pointer on use, or assume pointers cannot be nil. The former is really annoying, and the latter requires something which I did not want to do (which you will most likely not agree with just because it doesn’t seem like a bad thing from the start): explicit initialization of every value everywhere.

That doesn't sound right: the third option is making the compiler check that only initialized memory is read.

rzwitserloot•21h ago
That boils down to the second point, doesn't it? Just - with tooling that checks you are doing the thing that the model requires you to do.
mcny•21h ago
From a web developer's perspective, I think the problem is that the people who built these systems were too nice.

Let me explain.

Web browsers have the option to reject syntax errors in HTML and immediately fail, but they don't. Similarly, I think compilers could be a bit slower, check a few more things, and refuse to produce an output if a pointer could be null.

I'm just a code monkey, so I don't have all the answers, but is it possible to have a rule like "when in doubt, throw an error"? The code might be legal, but we aren't here to play code golf. Why not deny such "clever" code with a vengeance and let us mortals live our lives? Can compilers do that by default?

nixosbestos•21h ago
This... is why Rust exists, yes.
adastra22•20h ago
We do that, and then people complain the type checker is too hard to satisfy, and go back to dynamic languages.
delaminator•20h ago
A web developer without knowing the full history.

Netscape Navigator did, in fact, reject invalid HTML. Then along came Internet Explorer and chose “render invalid HTML dwim” as a strategy. People, my young naive self included, moaned about NN being too strict.

NN eventually switched to the tag soup approach.

XHTML 1.0 arrived in 2000, attempting to reform HTML by recasting it as an XML application. The idea was to impose XML’s strict parsing rules: well-formed documents only, close all your tags, lowercase element names, quote all attributes, and if the document is malformed, the parser must stop and display an error rather than guess. XHTML was abandoned in 2009.

When HTML5 was being drafted in 2004-onwards, the WHATWG actually had to formally specify how browsers should handle malformed markup, essentially codifying IE’s error-recovery heuristics as the standard.

So your proposal has been attempted multiple times and been rejected by the market (free or otherwise that’s a different debate!).

DarkNova6•20h ago
It's more complicated than that:

1: C uses pointer arithmetic and any pointer can end up being nil or simply show to the wrong address.

2: Just because memory is initialized, that doesn't mean a correct value is read. Some types don't have a valid zero-state (think of Rational-Numbers or Date). Which is why you need a way to express optionality and want to throw an exception if it the empty-state is accessed.

jonathanstrange•21h ago
I don't believe it is possible (or even makes sense) to completely avoid illegal/invalid objects and structures, and a cheap way to mark these are nil pointers. From that perspective, it doesn't matter to me whether a language allows nil pointers or doesn't have them. What matters are the details, though. It is generally desirable to keep invalid objects properly typed and languages with nil sometimes have quirks with "nil typing" (I'm not sure if that's the right term).
carlmr•21h ago
>It is generally desirable to keep invalid objects properly typed

I think that's the bigger issue in Java. `int` can't be used in generics, so now you use the boxed type `Integer` which needs to be checked on each use, which depending on the usage is a lot of work.

If Java had proper generics without this workaround, we wouldn't have nearly as many issues.

mrkeen•19h ago
What would change your mind regarding ruling out illegal structures?

In an alternative universe, there could be n sentinel non-values, instead of a single null.

There could be "null" ("I am initialising this memory as uninitialised"), but there could also be "egad" ("this pointer refers to memory on a different page"), there could be "biff" ("this address is on another caller's stack").

There are infinite ways you could design a language which lets you take invalid memory and tell the type system it's a Sheep, when it's not. Some of those languages might even have sophisticated biff-checkers or raise EgadReferenceExceptions.

What's stopping you from throwing out null along with biff and egad?

naasking•19h ago
> I don't believe it is possible (or even makes sense) to completely avoid illegal/invalid objects and structures, and a cheap way to mark these are nil pointers

Obviously we should be able to represent objects with possibly invalid state, but rather than allowing a "possibly invalid value" as a legitimate member of a huge number of types, which is what nil/null permits, the possible invalidity should be it's own type, like a MaybeInvalid<T>. Even languages that bake null checking into the type system are doing this, and then just inserting implicit conversions to/from such a type.

tialaramex•17h ago
WUFFS is an example of a language which just doesn't have any of these "illegal/invalid objects".

Safe Rust doesn't have any either, though presumably you'll say "but unsafe" there.

> It is generally desirable to keep invalid objects properly typed

As written this doesn't mean anything, if the object is properly typed then it is valid, an invalid object can't be properly typed. So more likely you're imagining something like Rust's MaybeUninit<T> type wrapper which says well, either this is a T, or it isn't (yet) and so a programmer will need to tell the compiler once they've arranged that it is a T so then it can be used as a T.

Barry Revzin has (I think) landed the fixes to the C++ type system so that you could reasonably write this in C++ too once you have C++ 26.

K0nserv•21h ago
> If you want to make pointers not have a nil state by default, this requires one of two possibilities: requiring the programmer to test every pointer on use, or assume pointers cannot be nil. The former is really annoying, and the latter requires something which I did not want to do (which you will most likely not agree with just because it doesn’t seem like a bad thing from the start): explicit initialization of every value everywhere.

To me this is the crux of the problem with null. It's not that null itself is a problem, it's that nothing communicates when it can be expected. This leads to to anti-patterns like null-checking every single pointer dereference. What's need is a way to signal when and when not `null` is valid under your domain model. This is precisely what stuff like `Option` does. Without a signal like this, programming feels like a minefield, where every dereference is liable to blow up, personally I'm done with that kind of programming.

The latter part of the post about individual-element vs grouped-element mindset is interesting, but I'm not sure it refutes the need for null or reasoning about it.

EDIT: It's also worth noting that Rust can still zero initialise entire structs despite element-wise initialisation when the valid bit pattern for the struct is 0.

DarkNova6•21h ago
I think this is about 2 problems coming together:

- 1: In C (and relatives), you cannot rule out that any pointer is not-null. Simply due to pointer arithmetic.

- 2: Some values have no default-zero state.

On #2 I found the EG discussions on the Java mailing lists to be fascinating. Think of innocuous types such as "date", where a default value only causes problems. You end up with something like "1970:0:0" or "0:0:0" and it acts like a valid date, but its use is likely unintentional and leads to follow-up issues.

And once you have multi-threading involved, even a simple null-flag becomes a difficult technical challenge to implement under all conditions.

K0nserv•20h ago
While null-pointers are possibly under #1 it seems much more likely that you'd produce other kinds of invalid pointers(out of bounds, unaligned etc) than nullptr. The use of null pointers to signal absence and failure is surely the most common source of them in C (and relatives).

I've always understood the billion dollar mistake to be more about #2 and language like Java in particular. Agree about default values being bad, it's one of my primary reservations with Go.

DarkNova6•9h ago
> While null-pointers are possibly under #1 it seems much more likely that you'd produce other kinds of invalid pointers(out of bounds, unaligned etc) than nullptr. The use of null pointers to signal absence and failure is surely the most common source of them in C (and relatives).

Fair point. Still, it just leaves a bitter taste when you want to express something as non-null but can't technically exclude it...

preommr•21h ago
I like giner bill, but:

- this is experienced/smart person forgetting about selection bias - yea null ptr errors are easy when you've had years to learn to avoid them.

- systems programmers are just as bad as web devs in forgetting that that their style of programming doesn't always generalize across multiple domains.

- I don't think the article puts enough emphasis on how big of a difference tooling makes; compilers are smart enough now to tip off about lots of errors that manifested as null pointer errors - like forgetting to initialize a variable, that's just a lint error now.

- I also don't understand the obsession with being so strongly tied to C. Like I understand carbon, because that's a direct replacement that google is working on and because c++ has the opposite problem of having too much such that there's a really great language somewhere in the mess, but people aren't seriously going to jump from c to odin. People that like c, use c, are going to stay with c. People that try new languages want something at least a little bit better. The outcome could be the same, but it should be because a feature is good and not as a compromise in the hopes that it'll lure people over.

fauigerzigerk•21h ago
I agree that null pointers have never been this huge problem compared to many others.

But I think it is rather convenient to formally specify which pointers can be null and which ones can not.

How do I tell the users of my library wether they can pass null into my API? How do I stop people from putting "just in case" null checks into code that doesn't need it, obscuring the logic of the program and burdening callers with yet more error handling?

In my opinion, the "modern" approach of using some sort of Maybe type is just far more productive.

ant6n•20h ago
Didn’t c++ solve this problem with references vs pointers? References shouldn’t be null, pointers could be.
mojuba•19h ago
It didn't make pointers safer to use though. In Swift and some other modern languages you can't dereference an optional (nullable) pointer without force-unwrapping it.
amelius•19h ago
You can still fabricate a null reference IIRC.
Rexxar•19h ago
It always involve an undefined behaviour at creation of the 'null reference', there is no legal way to create it.
amelius•17h ago
True. In any case references solve only half of the problem because it lets you state "this function will not take a null pointer". You still cannot say "this function may take a null pointer" unless you use a very unusual convention of saying that any pointer argument may take a null pointer.
widdershins•16h ago
I don't find that convention unusual. That's how I (and everyone at my company) writes code every day. If an argument is a pointer, that means it may be null. If it may not be null, it should be a reference.
amelius•13h ago
And the libraries that you use?

E.g.,

std::size_t std::strlen(const char* str);

PaulHoule•20h ago
It's a really common pattern that you want to write

   Object value = a.b.c.d;
for something like a JSON value you got over the web where any of it might be null but have to write something like

   Object value = nonNull(a) ? nonNull(a.b) ? nonNull(a.b.c) ? d : null : null : null
or

   Object value = null
   try {
      value = a.b.c.d
   } catch(NullPointerException x) {}
Just being able to write something like

   const value = a?.b?.c?.d;
is a great relief. If it is just value lookup letting a.b return null if a is null would be fine with me but there is something a little creepy about a.doSomething() doing nothing and returning null in that case.

Personally I am not a fan of Optional<T> and (worse) Either<A,B> in Java for various reasons. I think the official functional stuff in Java (like the streams API) is awful, but I like working with functions like

   Object value = nullSafe(a,x=>x.b,x=>x.c,x=>x.d)
The author has some affinity towards a collection-first or collection-only style of programming and certainly I have written programs or subprograms working with dynamic structures that leaned heavily into List<T>, that is, a value which might be present or absent can be treated as a List which just happens to have 0..1 members and x.map(y=>...) and other functional operators do the right thing for the 0..1 and 0..N cases and if you use the same set of operators for both of those cases they just roll off your fingers and you are less likely to forget to put in null checks and when you compose more complex operators out of simpler ones it tends to "just work"
tekne•19h ago
My personal preference would be to have separate types `A` and `A?` and overload field projections to work on `A?` as you'd expect. Subtyping optional.

Putting on my theory hat, while we we can overload field projection for any monad (data structure representing a result + an effect : think result for exceptions, or a thunk for a promise), it's not the best idea.

But for what is at least morally a commutative (order doesn't matter: FAIL ; pure = pure ; FAIL = FAIL) and idempotent (FAIL ; FAIL = FAIL) monad, it works...

Which justifies fun things, like lazy projections on promises!

PaulHoule•19h ago
... or maybe in the definition of type A you can specify what

   ((A) null).someValue
or

  ((A) null).doSomething()
does at the field or method level. I guess there is the issue of how you figure out what the type is which depends of course on the language. Could be a better fit for C++ than all-virtual Java but I don't want to give the C++ any ideas to make that language more complex!
tekne•17h ago
You could, but the issue is that now when I'm defining a type I need to think about it's nullable behaviour.

Versus if I instead view nullability as a way of transforming types (aka a functor) that works by reflection; this gives me some parametricity: the behaviour of my function will be the "same" regardless of which generic I slot in (though fields don't play that well with generics... something something row polymorphism something something).

Ill formed thoughts really; what I'm handwaving at is I slightly agree with the anti-complexity crowd that an operator should usually do the same thing, and yet I think it harms terseness and hence often readability and understanding to have non-overloaded boilerplate everywhere (addf64, addu32, addi16...).

Parametricity is a nice compromise for non-primitive ops, imo.

PaulHoule•14h ago
Yeah, there are two ideals I think of in terms of programming languages:

(1) As a simple set of primitive operations that I can use to build up increasingly complex operations and on that level I value "simple". Like the all-virtual method dispatch in Java as compared to whatever it is in C# or C++. In that case I value predictability and users knowing what they're going to get.

(2) As a platform for domain-specific languages such as the tactics described in On Lisp although those tactics apply well to other languages. In that case my desires as an "author of libraries" and "users of libraries" start to diverge. With that hat on I start to think (a) application programs can consist of "islands" with radically different coding styles (say in Java part of the system is comfortable with POJOs but other parts work with dynamically structured data with no schema or a schema defined at runtime) and (b) I'd like to be able to bend the language really far.

From the viewpoint of a programmer who wants to get work and have an impact on the industry I'm usually working in commercially mature languages and finding as much of those values in them as I can.

onionisafruit•19h ago
A function argument might be a pointer for a few reasons. Is it optional? Is it to avoid copying a huge value? Is it so it can be mutated? Or, my favorite reason, maybe the function was written to be called by another function that happens to already have that value as a pointer.

So yes, I wish there was a commonly used way to express which of these properties of a pointer will be exploited by the function.

01HNNWZ0MV43FF•19h ago
C is such a Blub language lol

Optional is `Option<T>`

Zero-copy is `&T`

Mutation is `&mut T`

Diehard C programmers have Stockholm Syndrome for the language because they like to show off how they can be productive in a bad language. If they took a few months to learn C++ / Rust / C# / any language that has solved this, they'd have to admit that they staked a lot of their ego on a language that constantly makes them jump through hoops. Because they love showing that they're good hoop-jumpers.

But any noobie who's a year into programming will say "Oh cool, in those languages I don't have null pointer exceptions" and never learn C. Good!

alfiedotwtf•13h ago
If only I had a dollar for every time I’ve heard “you just need a different mindset” and “you just to be better at C” as an excuse as to why “we don’t need rust”, it’s always been from the same people who have CVEs against their own C code failing to do exactly what at they say they could never do in real C code
rwmj•18h ago
While the "modern" (actually, 1970s, from ML) approach with option types is vastly superior, modern C compilers do let you declare that function parameters should not be NULL. In GCC or Clang, add __attribute__((nonnull)). There are still issues[1] with this, but it's sufficient for most code.

[1] One big issue is that GCC takes this hint and uses it in the optimizer, so you cannot have both a compile time and a run time check for a non-NULL parameter.

rnentjes•20h ago
After reading all that (ok skimming), he does mention nullable types, but doesn't go into them at all. What's wrong with having the option of defining if something can be null or not? This is how I understand nullable types, don't known about monads. You only have to check if it's null if you have defined that it can be null.

To me this sounds like a solved problem, for example like how it's done in kotlin: https://kotlinlang.org/docs/null-safety.html#nullable-types-...

christophilus•20h ago
Yeah. C# has this ages ago— maybe from the beginning- it’s solvable in a variety of ways at the type system level.

The remaining challenge is interfacing with unsafe code. Unsafe code should be required to check all pointers for null before allowing it back into the safe parts of the codebase.

tialaramex•14h ago
C# 2 added "nullable value types". For example bool? is a type which can be true, or false, or null. Does my cat like dry food? True? False? Actually I do not have a cat so that's null. Historically no value types were nullable. In C# 1.0 the bool could only be true or false.

C# 8 added "nullable reference types". For example string? is a type which can be any string, including the empty string "", or it can be null. What's the name of my oldest report? "Steve" ? "Mary" ? No, I do not have anybody reporting to me, it's null. Historically all reference types were nullable, it wasn't optional. In C# 1.0 any string could be null, whereas in C# 8 you could explicitly say that some strings mustn't be null.

However almost all C# software runs on the CLR, and the CLR doesn't believe in these rules, so where your C# is called by other software, possibly not even written in C# you do need to know that all your function boundaries don't actually enforce null checks. This matters most if you write libraries used by third parties in other programming languages, and least for in-house or personal stuff where it clearly goes in the "Don't do that" pile.

Perhaps think of a C# 8.0+ string parameter as a stern "Do not use null strings" sign rather than a firm language commitment like Rust's &str or a C++ &std::string. That new intern will obey it, that nerd who writes machine code for fun will at least know it's their fault if they don't obey it, but the customer on another continent who has apparently never read any of your documentation can't be relied upon to uphold this constraint.

[Edited: fixed numerous typos, there are doubtless more]

mh2266•19h ago
Right, he says:

> If you want to make pointers not have a nil state by default, this requires one of two possibilities: requiring the programmer to test every pointer on use, or assume pointers cannot be nil. The former is really annoying, and the latter requires something which I did not want to do (which you will most likely not agree with just because it doesn’t seem like a bad thing from the start): explicit initialization of every value everywhere.

In Kotlin (and Rust, Swift, ...) these are not the only options. You can check a pointer/reference once, and then use it as a non-nullable type afterwards. And if you don't want to do that, you can just add !!/!/unwrap: you are just forced to explicitly acknowledge that you might blow up the entire app.

PunchyHamster•20h ago
Well, probably trillion dollar one by now; It's not just the null pointer bugs that waste time, it's the fact language is designed around them that's the problem

> null pointer dereferences are empirically the easiest class of invalid memory addresses to catch at runtime, and are the least common kind of invalid memory addresses that happen in memory unsafe languages.

and yet I still see them popping up in memory-safe languages

> In statically typed compiled manual-memory managed languages, it’s not as much of a problem empirically compared to the set of invalid memory address problems, of which most of them are solved with a different mindset.

that's moving goalpost away from original claim; it wasn't "billion dollar mistake" in context of ALGOL, it was one because oh so many other languages, static or dynamic, managed or unmanaged, copied that behavior

woodruffw•19h ago
I don’t understand the premise: null references (of whatever flavor) can simultaneously be trivial to detect at runtime and have been a poor design decision. The two aren’t mutually exclusive.

I think Hoare’s own quote has exactly that nuance in it: reliability and predictability are given as much prominence as security.

noelwelsh•19h ago
I really didn't like this article.

Using scare quotes everywhere just makes it read like the author is engaging in bad faith. And I don't think they really address the issue.

The discussion in the "The Problem of the Individual-Element Mindset" section seems fairly arrogant, and ignorant of the economic realities of why people don't use manual memory management. "Individual-Element code" is not stupid, as they claim, but optimizing for other criteria than performance.

Their core arguments seem to be 1) I don't want to program in a way that excludes null pointers and 2) non-nullable references preclude arena-based memory management.

Regarding 1) you cannot make any useful statements. Their preference is their preference. That's fine and it's a fair argument as far as I'm concerned; they can create the language they want to create.

Regarding 2), you can easily distinguish nullable and non-nullable references in the type system. At the more experimental end are type systems that address these problems more directly. OxCaml has the concept of modes (https://oxcaml.org/documentation/modes/intro/) that track when something has happened to a value. So using modes you can track whether a value is initialized, and thus prevent using before initializing. Capture checking in Scala (https://docs.scala-lang.org/scala3/reference/experimental/cc...) is similar, and can prevent use-after-free (and maybe use before initialization? I'm not sure.) So it's not like this cannot be done safely, and I believe OxCaml at least is used in production code.

mh2266•19h ago
The section "The Problem of the Individual-Element Mindset" bugs me quite a bit, the core of it being:

> This architectural mindset does lead to loads of problems as a project scales. Unfortunately, a lot of people never move past this point on their journey as a programmer. Sometimes they do not move past this point as they only program in a language with automatic memory management (e.g. garbage collection or automatic reference counting), and when you are in such a language, you pretty much never think about these aspects as much.

Billions of dollars worth of useful software has been shipped in languages with garbage collection or ARC: roughly the entire Android (JVM) and iOS (ARC) application ecosystems, massively successful websites built on top of JVM languages, Python (Instagram etc.), PHP (Wikipedia, Facebook, ...).

In game development specifically, since there's a Casey Muratori video linked here, we have the entire Unity engine set of games written in garbage-collected C#, including a freaking BAFTA winner in Outer Wilds. Casey, meanwhile, has worked on a low-level game development video series for a decade and... never actually shipped a game?

CyMonk•18h ago
> Casey, meanwhile, has worked on a low-level game development video series for a decade and... never actually shipped a game?

He worked with Jonathan Blow on "The Witness"[0].

As the developer of the "Bink 2" video codec[1] and the animation tool "Granny 3d"[2], his code powers thousands of games.

[0] https://store.steampowered.com/app/210970/The_Witness/, [1] https://www.radgametools.com/bnkmain.htm, [2] https://www.radgametools.com/granny.html

wasmperson•17h ago
I don't think Casey has ever claimed to be the developer of Bink 2. He usually brings it up when explaining the kind of work that was performed at Rad Game Tools, then explicitly states that his work was largely on Granny 3d in particular.
CyMonk•10h ago
"Bink [...] was written and researched by [...] mostly Fabian Giesen, Casey Muratori and Jeff Roberts.", Bink Video Credits

https://www.radgametools.com/binkhcrd.htm

badsectoracula•18h ago
> the entire Unity engine set of games written in garbage-collected C#, including a freaking BAFTA winner in Outer Wilds.

Some of those games (though not all of them, unfortunately) try to work around C#'s garbage collector for performance reasons using essentially adhoc memory allocators via object pools and similar approaches. This is probably what this part...

--- And if you ever do think about these, it’s usually because you are trying to do something performance-oriented and you have to pretend you are managing your own memory. It is common for many games that have been written with garbage collected languages to try to get around the inadequacies of not being able to manage your own memory ---

...is referring to.

> Casey, meanwhile, has worked on a low-level game development video series for a decade and... never actually shipped a game?

These videos are all around 90-120 minutes long, each posted with gaps between them since the previous (my guess is whenever Casey had time) and the purpose and content of these videos is pedagogical so he spends time explaining what he does - they aren't just screencasts of someone writing code.

If you combine the videos and assuming someone works on it 6h/day with workdays alone it'd take around 8-9 months to write whatever is written there but this also ignores the amount of time spent on explanations (which is the main focus of the videos).

So it is very misleading to use the series as some sort of measure for what it'd take Casey (or anyone else, really) to make a game using "low level" development.

mrkeen•18h ago
> Muratori ... never actually shipped a game?

Ahh, I was just thinking about this morning.

Remember the Muratori v Uncle Bob debate? Back then the ad-hominems were flying left and right, with Muratori being the crowd favourite (a real programmer) compared to Uncle Bob (who allegedly didn't write software).

Then a few months ago Muratori gave a really interesting multi-hour talk on the history of OOP (including plenty of well-thought out criticism). I liked the talk, so I fully expected a bunch of "real programmers" to shoot that talk down as academic nonsense.

Anyway, looks like Muratori is right on schedule to graduate from programmer to non-programmer.

valiant55•18h ago
Unity isn't written in C#, it's C++. C# is used as the scripting engine.
mh2266•17h ago
Yes, of course, but the actual games are written (primarily! sometimes they will optimize something when actually necessary with a lower-level language! that's great!) in the scripting language.

The OP implies heavily that writing a program in a language with anything but pure manual memory management makes you lesser as a programmer than him: "Unfortunately, a lot of people never move past this point on their journey as a programmer" implies he has moved further on in his "journey" than those that dare to use a language with GC.

(and with respect to C++ note that OP considers RAII to be deficient in the same way as GC and ARC)

tialaramex•11h ago
Indeed to the extent that Casey has a point here (which sure, I think in its original context it was fine, it's just unfortunate if you mistook a clever quip at a party for a life philosophy) C++ is riddled with types explicitly making this uh, choice.

Its not near the top of the list of reasons std::unordered_map is a crap type, but it's certainly on there. If we choose the capacity explicitly knowing we'll want to put no more than 8340 (key,value) pairs into Rust's HashMap we only allocate once, to make enough space for all 8340 pairs because duh, that's what capacity means. But std::unordered_map doesn't take the hint, it merely makes its internal hash table big enough, and each of the 8340 pairs is allocated separately.

eloisant•19h ago
Runtime errors are much, much worse than compilation errors and that's why it's such a concern.

There is a dev cycle of local dev, CI, deploy to staging, deploy to production. The later you catch an error, the most expensive it is to fix.

Compilation errors are usually caught during local dev, or occasionally CI (if it's in a module that the dev didn't try to build locally).

A runtime error might not be caught until it reaches production, which not only causes a customer impact but now needs an emergency hotfix to push through the whole pipeline which can be a long process for big companies.

jerf•19h ago
The problem with NULL is that it is forcibly adjoined as a value the compiler considers valid to every pointer type. In the languages in question, you can't have a non-nil pointer, no matter how much you might want one.

I do agree that it is, on its own, not necessarily the large problem it is made out to be. One nice thing about NULL dereferences is that they tend to be visible and obvious. They crash things and make lots of noise. As invalid values go, they're actually on the friendlier side. The invalid values that silently sail through your program, corrupting everything they touch, but never crashing anything, are far more dangerous. In fact one of the worst possible solutions to a NULL is to default it to some value, especially one like "0" that looks like half the other values in your program.

The dangers of NULL are somewhat oversold because they are cognitively-available dangers. We see the crashes. We see the failures. But those are far better than the failures we don't see, and the crashes we see only much later, sometimes years later. (Don't ask me about the legacy billing systems I've been trying to deal with.)

But was it a billion dollar mistake? Still yes, because having a type that forces this value on to the legal set is still a mistake and has still caused lots of problems that could have been avoided. In addition to that billion dollar mistake, there has also been a lot of code written that has made similar mistakes of allowing similarly illegal values representable in the code, and that has also been a huge mistake. Perhaps a larger one, perhaps not. Hard to get an objective measure of these two mistakes, both huge, both frequent, both highly impactful.

Ultimately, though, the NULL pointer is just a particular instantiation of the general problem of allowing illegal states to be represented, and of that class of problem, it is probably one of the least harmful examples of it... unless you overly avail yourself of all of those "a?.b?.c?.d" operators and "upgrade" your NULL issues into something that sprays illegal and/or incorrect values through your code instead. I really don't like those things being so convenient to hand, they encourage people to jump from the frying pan into the fire too often.

simon_void•19h ago
I really like languages with non-/nullable types like Kotlin and Rust! I used to work with Java for over a decade and therefore used to code very defensively against NullPointerException and now (with Kotlin) I just don't have to do this anymore. That being said there are two possible sources of nullpointers: from inside your program or from outside (e.g. the json your server got send wasn't fully initialized). The majority of NullPointers come from outside in my experience. Nullpointers from within you have to fix mostly once. but there's no end to what data send to you from outside your control can be missing. Of course you still have to validate/parse outside data when using Kotlin but since the type system then carries on which data has been validated, you can drop the defensive mindset that was appropriate in Java.

So if you have a self-contained, non-safety critical project and want to use/try out an arena-allocated memory approach using Odin seems fine (e.g. looking at you: "raytracing in a weekend").

So yeah, I think there's a niche of projects where the possibility of null pointers isn't a huge deal.

None of my work-related projects fall in that category though :D

naasking•19h ago
One of the rare cases where the answer to a headline question is "yes".
df0b9f169d54•19h ago
why does the article re-use a lot of talk from Casey's video without quotes? that's very confusing who is talking what
cletus•18h ago
The mistake was not having nullability be expressed in the type system.

At Facebook I used their PHP fork Hack a lot and Hack has a really expressive type system where PHP does not. You can express nullability of a type and it defaults to a type being non-nullable, which is the correct default. The type checker was aware of changes too, so:

    function foo(?A $a): void {
      $a->bar(); // compile error, $a could be null
      if ($a is null) {
        return;
      }
      $a->bar(); // not a compiler error because $a is now A not ?A
      if ($a is ChildOfA) {
        $a->childBar(); // not an error, in this scope $a is ChildOfA
      }
    }
Now Hack like Java used type erasure so you could force a null into something non-nullable if you really wanted to but, in practice, this almost never happened. A far bigger problem was dealing with legacy code that was converted with a tool and returned or used the type "mixed", which could be literally anything.

The real problem with Java in particular is you'd end up chaining calls then get the dreaded NullPointerException and have no idea from the error or the logs what was broken from:

   a.b.c.d();
I'm fine with things like Option/Maybe types but to me they solve different problems. They're a way of expressing that you don't want to specify a value or that a value is missing and that's different to something being null (IMHO).
mirekrusin•13h ago
Exactly, null is not evil it itself – the fact that it's not represented in type system is.

Type system where nullability can be expressed, you have refinement so you can map "null | T" to "T" with conditionals and sugar like optional chaining and nullish coalescing is all that's needed.

hu3•11h ago
Neat.

In PHP land, for some years now that code would not pass CI/CD checks and IDEs show red squiggles. Provided they use any popular static analysis tools like PHPStan, Psalm and I believe SonarQube would also flag it.

stephenlf•18h ago
Are there tradeoffs between individual-element and group-element mindsets with regards to project scale? Does the group-element/global architecture mindset require holding the whole program in my head? Is that even possible for large projects?

I have no experience working in C. Obviously, some of the biggest and most important codebases on earth are C.

Neywiny•18h ago
Right off the bat, disagree. I always bring up this example but it's actually expanded. On some embedded systems, there is RAM at 0x0. It is writeable and readable. On even more, there's ROM there. Countless times I've had a null pointer dereference be not only a silent failure that wreaks havoc downstream but also a valid operation.

In other times, I've had a malloc fail because I didn't have my heap setup, the code I had pulled in didn't check, and there goes my interrupt vector table or whatever.

When I was making a simulator for one of these chips I explicitly turned off that section of memory do I could catch null pointer dereferences. Not trivial to do in practice

superblas•16h ago
+1 for this. On my system, an ARM cortex-M7, there’s RAM at address 0. In order to catch null pointer dereferences I ended up activating the MPU to make the first couple hundred bytes inaccessible (non-readable, non-writable).
Neywiny•13h ago
MPU stuff is on my to-do list to get read up on. I have had a decent amount of stack overflows, the aforementioned example (which was on an M7), and other stuff that it might help with. It's something I don't necessarily want to rely on it in production, but it could help for debug.
filoeleven•18h ago
The original title is "Was it really a Billion Dollar Mistake?"

The title should be updated to match the original per the HN guidelines.

ufmace•17h ago
I don't get the "individual" vs "grouped" element thing at all. I guess you could interpret everything as a list, where null is actually empty list, but it seems like that would be a ton of boilerplate and architecture astronomy, not a universally better way of programming. I don't see any decent explanation of what "grouped-element" programming is and why it's universally better and more advanced.

If that was really true, you should be able to point to a subset of code developed with this supposedly better mindset and demonstrate that it's actually better in terms of some objective measure like speed, memory efficiency, features, security vulnerabilities, etc. I don't see anything like that though. What are the odds that it really is a better way of programming versus a theory some guy made up? If you're familiar with the "no silver bullets" thing, a ton of things have been promoted by various people in the industry as a universal solution to general problems of software quality at various times, but vanishingly few of them have ever made a real difference.

It seems to me that advances in memory management have been one of the few advancements promoted over the decades to make programming genuinely better and more reliable. Witness the dominance of garbage-collected languages for virtually everything that could possibly be done with them, and the advantages of moving things that can't use GC to languages with more solid non-GC memory management like Rust, which are becoming more clear. I think that's the best element we can lean on for software quality, not this grouped-element stuff.

kjksf•15h ago
Here's an example. Imagine you're writing a parser that creates AST tree in C++.

What 99% of people do is the "individual" style where every node is allocate with new and the memory comes from generic OS allocator.

Then when you free the AST tree, you have traverse the tree and call delete on every node.

There can be thousands of nodes.

The crucial observation is: the lifetime of all nodes is the same. It's the lifetime of AST tree.

The "grouped" thing is: you have an allocator dedicated to nodes of the AST tree. All nodes are allocated from this allocator.

This has numerous speed and simplicity benefits.

You need one free() call to free allocator vs. thousands of free() for each node.

You can optimize your allocator for that use case compared to general OS allocator. By definition it's a bump pointer allocator (i.e. allocation is mostly addr += sizeof(obj)) which is much faster than what even fastest general allocators can do.

You don't need to track per-allocation metadata that general allocator needs to be able to individually free each allocation. So you use less memory.

There's no fragmentation because your allocator is contiguous space.

The use of cache lines is most likely better because memory is not spread around. The nodes are used together so it's better if they are close in memory.

Those are very significant benefits and yet, as the article notices, very few people are aware of this.

Plus until very recently (before Rust became popular) the only serious low-level language was C/C++ and they don't help you programming in this style.

Odin, zig, jai do. They make an allocator an exposed thing and provide language and standard library support for using different allocators.

ufmace•9h ago
Okay, it makes sense that that's what it is. Still, it seems to me like that's more of an optimization technique that's applicable to certain specific scenarios rather than a mindset to view all software development as.

I have actually heard of something like that, as arena allocation, though mostly as something to use in a GCed language to either avoid or reduce the impact of GC cycles, also for certain specific scenarios.

CyMonk•9h ago
In this case the "grouped element mindset" means not only just using allocators but also avoiding RAII by flattening the traditional pointer- based OOP AST into arrays accessed by handles.
tialaramex•17h ago
It all starts to unravel badly once we get here:

> In theory, null is still a perfectly valid memory address

Crucially C's pointers are not addresses. So now we're at best talking only about Odin, which wasn't the subject of Tony's "Billion dollar problem" claim in the first place.

And honestly I doubt that in practice Odin's pointers are just addresses either because Odin uses LLVM.

wasmperson•17h ago
Muratori followers tend to beat around the bush to avoid saying it directly, but the implication here is that anyone who sees value in Rust is simply a bad programmer and will either stay a bad programmer or eventually stop using the language. Same goes for any language with garbage collection.

A quote from the linked video starting around 8:30:

> All of the advice you see online pretty much is for people thinking at this level, which is not where you want to be. You're gonna have to go through it at some point to understand what's going on, but your goal is to go from [individual element thinking] to [grouped element thinking], so advice like 'use smart pointers' or the rust borrow checker, all that stuff? Those are for people who are still not very good at programming.

To be clear: I do not agree with this position, but I thought it was important to clarify what I believe to be the intended message of this article (and others that you see coming from this group of people).

agentultra•11h ago
I’m not sure that the distinction between “element-wise” and “group-wise” mindsets are mutually exclusive in the design space for a language.

The set of programs that can express problems as transformations on arrays is quite large.

But I think there’s a large overlap with programs that also need to manage resource lifetimes as well where a resource could be a region of foreign-allocated allocated memory, a database connection, etc; a process-scoped object that can only be acquired (or not) at runtime.

Even in Odin you’re going to need to think in terms of arrays and how individual element lifetimes at some point depending on how you classify and structure data. The trade off seems to be that how you specify and maintain contracts for the lifetime of objects like sockets and handles are up to you and Odin won’t help you with that. In return you get array oriented initialisation?

Or am I misunderstanding the mental model?

alextingle•10h ago
His framing of individual-element mindset vs. grouped-element mindset is very clear sighted.

It's what frustrates me so much about Rust - the language puts so much focus and complexity onto something that you shouldn't be doing in the first place: allocating and freeing millions of tiny objects. You end up writing programs that spend half their time allocating, initialising, destroying and freeing elaborate trees of records, and devote half their memory to pointers.