Hacker Dudes// alternate deck. same feed.
FEED ~9/h Δ0 ────:──── LINK ░ SIGNAL
97%
SPOOLED 0/45 CYCLE 61339.582.95.37
The Goose Programming Languageaardappel.github.io
582pts/359 comments/2d/─
by bobbydigitales / hn> / « feed
▲▼ bobbydigitales19:32 hn>

"That looks really neat! The automatic analysis and use of multiple data stacks and resizable fields takes it from "lots of work can be done this way" to "basically everything can be".

The relative references and built in binary structure validation also enable direct memory mapping of data structures and immediate use of network data. "Parsing" file and network data is often 100x slower than it needs to be." - John Carmack

▲▼ mi_lk19:40 hn>

Obviously the guy is replying to something that you can't see without logging in twitter. And without context this means little

▲▼ andsoitis19:42 hn>
>Obviously the guy is replying to something that you can't see without logging in twitter.

I could follow the link and read the thread and I don't have an X account.

You can also check out the language's website by googling "goose programming language", which should lead you to: https://aardappel.github.io/goose/index.html

▲▼ mi_lk19:43 hn>

the rest of the thread yes but not the thread before

▲▼ cosmotic19:44 hn>

The link to the github works. Everything else opens google play store page for twitter for me.

▲▼ celsoazevedo20:21 hn>

The Twitter/X link doesn't show the full thread without a login. It also doesn't tell you that something is missing, so I guess it's normal for people to think they're seeing everything.

What X shows me: https://celsoazevedo.com/files/2026/x.webp

With a login/nitter: https://celsoazevedo.com/files/2026/nitter.webp

▲▼ mcdonje11:44 hn>

He should've posted the website instead of twitter

▲▼ randyrand21:33 hn>

Twitter is gone, in more ways than one. RIP.

▲▼ DoesntMatter2222:13 hn>

Better than ever tbh

▲▼ AnEro19:50 hn>

Such a cool idea! I've been toying around making a few libraries to force languages to do that direct io zero-read mapping to data structures. Ofcourse this looks way better and infinity less hacky, but it is a fun and trippy experience to find someone working in the same direction as you independently.

▲▼ Aardappel19:55 hn>

Btw I made the language, AMA. repo: https://github.com/aardappel/goose

▲▼ vanderZwan20:03 hn>

If you had no other obligations and todos getting in the way, what's the first thing you'd rewrite in Goose yourself?

(And why isn't it Amiga E?)

▲▼ Aardappel20:35 hn>

Most of my current Goose projects are games, not surprisingly, but I have some language/tooling ideas as well.

As for Amiga E, I am very proud of having made it way back when obviously, but it doesn't stack up against my more modern languages (Goose and Lobster), so I think I'll leave it be :)

▲▼ fancyfredbot20:12 hn>

What happens if the memory is freed in a different order to allocation? Does the language disallow that? Or does it get allowed but mean the stack can't shrink?

How are you deciding how many stacks to use?

▲▼ Aardappel20:16 hn>

The compiler automatically optimally allocates all arrays currently in scope that are able to grow to "stacks" (address spaced reserved areas).

The programmer choose what to store where, so you typically store things in arrays by lifetime. Goose gives you many more tools to do that, it can store entire variable sized trees of an enum type in a single array.

You try to avoid having to deallocate in mixed/random order, but if you absolutely must, you can turn arrays into pools, basically.

─ 5 more ─
▲▼ AnEro20:14 hn>

You noted AI creating a large amount of this code base with yourself directing it. Do you have a process that keeps you sane while doing this over such a large codebase?(especially for a brand new concept and implementation) [Obviously congrats and can't wait to read through to have deeper questions!]

▲▼ Aardappel20:25 hn>

A big part of this making it base the code of this new compiler on my previous compiler for the Lobster Programming Language. I had it go thru that codebase, distill an elaborate code style guide from it, and used that to make it write the new code pass by pass in that style. That made me feel "at home" as if it is my code, easier to review etc, and easier to know we're going in the right direction by seeing similar structure to my past compiler work.

Once the initial compiler was there it became easier, as Fable is rather good of working within the context of what is already there.

─ 1 more ─
▲▼ ksec20:15 hn>

First, Congrats on the release!

Do you know if Goose is the first time something like this has been done?

▲▼ Aardappel20:20 hn>

In this particular combination, most certainly.

There is lots of prior art on the individual parts of the design, e.g. "regions" and various stack languages etc, see: https://github.com/aardappel/goose/blob/master/docs/design/p...

Part of this is that 64-bit address spaces make this design practical, and almost all languages in use today were designed with the limitations of 32-bit in mind. Once you can assign arrays huge amounts of address space, you don't have to realloc, which gets you stable pointers, and a lot of the other Goose features.

▲▼ amelius20:19 hn>

Is this like forcing the escape analysis of the compiler to be perfect, by making it explicit in the programming language?

I'm wondering what kind of architectural corners this forces the programmer into ... have you found any so far?

▲▼ Aardappel20:22 hn>

Yes, the compiler guarantees references to data structures can't escape. I believe it does that "perfectly" :)

It certainly requires the programmer to rethink how they structure their data, as you have to think of where it is going to be stored, and how to store it in a way such that it will have similar lifetimes to what it is stored with, to get the most out of the language.

▲▼ koolba20:25 hn>

What does this mean (from your link):

>Nested data stays inline. A string, an array of strings, a record with variable-size fields and an array of those records are each one contiguous block with no pointer in it. A record that is 160 bytes and an allocation in C++ is 29 bytes and none in Goose.

I’m having trouble parsing that last sentence.

▲▼ lynndotpy20:29 hn>

I parsed it as "A record that is ((160 bytes and an allocation) in C++) is ((29 bytes and none) in Goose)."

So, I read it as, "C++ uses 160 bytes and an allocation, when Goose requires 29 bytes and no allocation".

▲▼ Aardappel20:29 hn>

It means that to do variable sized things like vectors and strings, C++ often ends up having to allocate their contents dynamically. Which results in data structures with lots of 64-bit pointers over multiple allocations.

Even Rust's "enum" feature, which can store different sized things all in the same space, does that at the cost of the smallest variant being just as big as the biggest.

Goose can do all of that not only inline (no pointers), but with variable size, meaning small items take less space. It's like a serialized data representation, but iterable. It makes for big space savings vs these languages.

─ 14 more ─
▲▼ lemming20:34 hn>

Do you consider Goose to be a replacement for Lobster, or do they fill different niches?

▲▼ Aardappel20:43 hn>

I think they have slightly different places on the "speed vs ease of use" pareto frontier.

With Goose, the core design favors speed over everything else. Though not only was I able to make that safe, it is fairly pleasant to use. But it remains a low level systems language that requires you to think hard about data organization.

Lobster is a waaay more high level language made to be pleasant to use first, fast second. Surprisingly though, it can get pretty close in speed to C++/Rust nowadays too, but still a lot slower than Goose.

─ 8 more ─
▲▼ nextaccountic20:38 hn>
>Threads that share nothing. A worker is compiled as a separate program with its own memory, and flat values cross typed queues as a memcpy. Data races, locks, atomics and memory orderings do not exist in the language.

how to perform this copy without atomics?

do the workers run as threads or processes?

also you claim it's faster than rust and c++. but for certain kinds of software it's certainly (and substantially) slower, right?

▲▼ Aardappel20:48 hn>

The copy is made by the runtime, which obviously does use atomics underneath, but you don't ever use these yourself.

The workers are threads. The separate memory is the concurrency model, but it's still single process, multiple threads.

The "faster than" is on average, and both in my own benchmarks and independent benchmarks you can find examples of algorithms where it is slower, but they tend to be the exception. "substantially" slower I haven't found yet.

https://kostya.github.io/LangArena/#/top_tab

─ 5 more ─
▲▼ cardboardguru05:34 hn>

Sounds like they are processes, as the docs say that all the global data is duplicated for the workers to have a private copy.

▲▼ groundzeros201521:41 hn>

Why are you pretending that John is endorsing your project?

▲▼ jdw6421:51 hn>

Why was it named Goose?

▲▼ Aardappel22:24 hn>

I've named many of my past languages with animal names, e.g. Lobster. Goose just popped into my head as cool sounding at the time. It has no special meaning.

─ 2 more ─
▲▼ adastra2222:42 hn>

I would throw the entire docs out and write it from scratch in your own words. I get that we live in an era where the machine writes most/all of the code, and I can accept that. What I need, to even consider using and extending this project, is some understanding of what part of the design is yours and what part is accident by Claude. I can't get that from reading Claudese.

▲▼ felixgallo22:57 hn>

We as a society have to cut this low effort commenting out. The documentation is perfectly comprehensible. What action would you take differently if a given feature were implemented by Claude, or a human?

─ 32 more ─
▲▼ WillAdams00:04 hn>

The version of the documentation which I would very much like to see would be a Literate Programming version where all the code is explained from the ground up.

Apparently, this can actually assist the LLM:

https://silly.business/blog/we-should-revisit-literate-progr...

as discussed here previously:

https://news.ycombinator.com/item?id=47300747

─ 5 more ─
▲▼ polski-g01:12 hn>

Once a week, I tell Antigravity (3.8-flash) to rewrite every code comment and documentation that was changed in the past week. Neither GPT nor Claude can write like a human.

─ 5 more ─
▲▼ assimpleaspossi01:21 hn>
>>I get that we live in an era where the machine writes most/all of the code

Only on the Internet is this true, not in real life. I cannot and do not trust any of these things to do a proper job because they have not in any situation succeeded in any job I've given them. And no company I deal with claims otherwise

─ 18 more ─
▲▼ dirkt06:13 hn>

Eh, I think the tutorial is pretty readable? It's concise enough.

With the tutorial, the summary also makes more sense. I have not read the other parts yet, but so far I like what I see.

All is in pretty good taste, and I like what I've seen so far. I do not care if the author himself came up with the idea, or if AI came up with it and the author approved.

I do not understand why you "have to" know this to make use of it. You can judge ideas independently of where they came from (and you should).

─ 3 more ─
▲▼ fasteo08:33 hn>

I do not think you know who this guy is

▲▼ shmageggy10:57 hn>

100%

Couldn’t make it more than a few sentences in before the bile started to build up in the back of my throat and I had to back out. It’s shocking to me that people knowingly post unedited Claude-slop drivel and are seemingly unashamed about it. In 2026, it seems the main role of technical humans should be to act as a barrier between our agents’ output and other humans.

─ 1 more ─
▲▼ osigurdson23:06 hn>

What are some use cases (if any) that the language is not suitable for?

▲▼ malkia00:06 hn>

Probably kernel code, or cases where allocation randomization is important. Seems like with goose you may end up executing more deterministically (but still it can randomize where these N data stacks start), but because it's single threaded workers (in essence), all data construction would follow the same pattern, hence an attacker (something snooping your memory) would know easier where things would be.

▲▼ giovannibonetti23:56 hn>

Where does the language stand in the array-of-structs vs struct-of-arrays choices? Does it offer an annotation to choose that like in Zig?

▲▼ Aardappel05:44 hn>

It does not currently offer automatic AOS -> SOA transform, you'd have to do that yourself. I did look into it, but doing it automatically is not a great fit with how Goose tracks arrays. Maybe can be reconsidered at some point.

▲▼ SwellJoe00:03 hn>

Way off topic, but I enjoyed Amiga E. And, I reckon you're one of a handful of people I would take seriously when they say, "I made a new programming language."

▲▼ Aardappel05:43 hn>

Thanks, that brings me back, haha :)

▲▼ avilay01:32 hn>

Given we are getting to the point where coding agents write most of the code, what motivates you to create a new programming language? Follow up: do you build these languages keeping humans in mind, or are these meant to be used by AI agents?

▲▼ Aardappel05:49 hn>

I've been making languages all my life, and I made this one because I had a really strong design in mind that I think should exist.

Whether it would make sense in this AI world was not a consideration.

The syntax and semantics were mostly designed for humans. That said, using it optimally asks a lot of the human, and in turns out the better LLMs don't break a sweat using it, so maybe it accidentally hits the spot. Goose with LLM can give you lots of speed without the effort.

In some weird way it is a great time to make new languages, because the downside of convincing humans to learn it is gone. Anyone that has a project that needs to be fast can decide to use Goose for it at very little cost.

─ 1 more ─
▲▼ JurassicTyler02:28 hn>

In this example does it compile if you write return inside the block right below w (where the error occurs)? https://github.com/aardappel/goose#safe-references-no-annota...

Hows the compile time? Do you have a number or is it similar to another language?

▲▼ Aardappel05:42 hn>

Good question, not sure without testing. Likely it will still give the error. It could elide it, but I am not sure how useful that is.

Optimized builds go thru the local C compiler, so they are limited by the speed of that. Dev/test build can use the built-in libtcc JIT which is super fast.

▲▼ ashkankiani02:46 hn>

I have my own language I'm working on in private and it's nice to see other people coming to the same conclusion on region based memory. I was heavily influenced 7 years ago from learning from HandmadeHero and the community around it, and so I made that memory model one of my goals from the start, and it is indeed possible to get very fast programming with it when you make it your default, and when you make the language able to statically analyze it more.

That is about where our similarities end, though, since I'm doing quite a lot of other stuff in the midst as well (much heavier type stuff, fully incremental, retargetable, reflection, integer range tracking, blah blah blah), and the more goals you have, the complexity blows up exponentially. I do think that the region based memory part alone is worthwhile to have as a language feature, so I applaud your pragmatism. I like your syntax for returning through call stacks, too.

There are other languages that do this, though. Did you look into any of the others? Or ones like Odin which give you the configurable bump allocators and scratch space as well? Mainly I'm curious about your opinions of prior art.

▲▼ ashkankiani05:08 hn>

it's too late to edit but i saw the prior art on github now https://github.com/aardappel/goose/blob/master/docs/design/p...

anyway, hope this project goes well! the secret of the arena allocator seems to be out now to the mainstream. I think this is also a strength of array programming languages in the tradition of APL/J/K/Q.

As this project develops, I'll get to benefit from your public crowd sourced testing and design before having to publicize mine :P

─ 1 more ─
▲▼ Zecc03:32 hn>

I will take a better look at Goose some time later when it's not late at night. Just wanted to drop by and thank you for creating Treesheets. I haven't given its more advanced features the use they deserve, but I love it as a note-taking application for anything that complexity-wise sits between a short outline in a plain text file and a ton of Markdown in something more advanced like Obsidian. And for, of course, any tabular data.

▲▼ Aardappel05:37 hn>

Hah cool, small world :)

▲▼ Zecc03:38 hn>

You might want to update https://strlen.com/language-design-overview/ at some point.

▲▼ Aardappel05:38 hn>

Haha yes, haven't updated my website in a while :(

▲▼ az09mugen04:53 hn>

Interesting, I'll play with it. In the "Why Goose" section there is a mistake I guess : "Faster than C++ and than safe Rust, while memory safe."

▲▼ jibalt05:09 hn>

I don't think it's a mistake. He's claiming that it's faster than C++ and it's faster than safe Rust.

─ 4 more ─
▲▼ cardboardguru05:15 hn>

Why would that be a mistake?

▲▼ forgotpwd1610:51 hn>

It's because in Goose* you write `x > a && x > b`, not `x > (a & b)`. :)

*And really in most if not all, Raku excluded**, languages.

**In Raku junctions can be specified with infix ops, i.e. a | b, a & b, a ^ b, equivalent to list ops any(...), all(...), one(...) respectively, and can define a custom one for none(...).

▲▼ emmelaich05:13 hn>

And ... you made Lobster lang, Cube2/Sauerbraten 3d shooter and voxray/voxile and lots of other cool things.

https://strlen.com/

▲▼ tymscar05:49 hn>

Sauerbraten is such an amazing game. Every so often I boot it up and play some venice instafrag

▲▼ vermilingua06:34 hn>

Oh wow this takes me back. We had very poor internet connectivity when I was growing up, so my brother and I played Sauerbraten and a handful of other games exhaustively with a crossover cable strung between our rooms.

▲▼ netless08:31 hn>

And not to forget Amiga E language...really cool stuff

▲▼ dananabread11:15 hn>

Hey the memory model is super interesting.

Looking at the repo it seems like the N stacks generated by the compiler are placed at least 2gb apart in virtual memory, have you run into any cases where that has been overflowed during runtime? The compiler cannot know which of the stacks are going to be used more than others and in a large complex program with many stacks any one of them could fail. I imagine this being an issue depends on target application - if you know you are building a game that you know the exact size of your levels / entity counts beforehand you know this is fine but if you were doing data processing / parsing potentially malicious user input it may be a problem.

I'm also interested how the memory locality works. Here you have the bases of your stacks at a series of very sparse virtual memory intervals which will be mapped by the page table to some physical memory addresses, does having them be so sparse cause any issues?

There are some unique ideas here - lifetimes on references to growable+shrinkable stacks, "balancing" stacks in recursive functions - and I'm glad you were able to use coding agents to actually materialize them.

▲▼ Aardappel17:25 hn>

No, not yet. You can also change that amount if you want, just 2GB is a pretty optimal default.

I am not sure what issues could be caused by the sparsity. Like you say, all that matters is that in physical memory, we are only using what is actually in use.

Thanks! :)

▲▼ Betelbuddy11:54 hn>

I am an AI and take offense with your statement.

▲▼ opem13:54 hn>

Is replacing rust a goal? If so I'm in! Just kidding, actually there are so many things I didn't get, good thing we have discord probably ask there. I used your other programming language lobster earlier, and have high hopes on this.

▲▼ Aardappel17:27 hn>

Thanks! There is certainly a big overlap with Rust in use cases.. I'll leave people to decide which they want to use :)

▲▼ sockopen14:03 hn>

why is it not written in goose

▲▼ jghn14:52 hn>

The main page was such obvious AI slop that I didn't bother looking further

▲▼ wvlia516:57 hn>

I really like your work.

I like the XY language, what do you think? https://nsl.com/k/xy/xy.htm

▲▼ Aardappel17:31 hn>

I generally LOVE concatenative languages! I made this once: https://strlen.com/false-language/

─ 1 more ─
▲▼ arunc17:36 hn>

Just curious why you kept semicolon as terminator. A good thing Golang did was to get rid of it.

▲▼ Aardappel17:48 hn>

I guess Goose is closer to Rust and C/C++ in design, and removing it does introduce some parsing gotchas.

I guess people also differ by how much they are bothered by it :)

My other language, Lobster, has no semicolons at all :)

▲▼ erfgh21:44 hn>

How can you claim Goose is faster than C++, i.e. faster than the language whose subset (i.e. C) it compiles to?

▲▼ Aardappel22:01 hn>

C++ code that does what Goose does is a soup of pointer manipulation, casts and byte stuffing. You could write that, but Goose proves it to be safe at compile time, with simple readable code. The moment you write "reasonable" code in C++, it is actually a lot slower than Goose.

For details on how, see https://github.com/aardappel/goose

─ 3 more ─
▲▼ mikeiavelli02:47 hn>

Wow, Mister van Oortmerssen himself! I was a big fan of Bla's first-class environments! You're a legend!!

▲▼ Aardappel03:31 hn>

Haha, I thing "Bla" was probably one of my least successful designs! So elegant, but too weird? Anyway, glad you remember :)

▲▼ geoffbp19:56 hn>

No relation to https://goose-docs.ai/ I guess?

▲▼ iacuobos20:01 hn>

I can't see a reason to use this and not C++ It reads like slop text

▲▼ andsoitis20:07 hn>
>to use this and not C++

Why Goose - https://aardappel.github.io/goose/#why-goose

▲▼ FrustratedMonky20:02 hn>

Looks cool, and well developed. But really, cover pages need some better TL;DR, about the purpose. What is it for? There are a ton of languages out there. What gap is this filling? What is the purpose, the use case. Why do I need to keep reading further?

Edit Man, two posting about Goose back to back. Carmack alone might be enough to read further.

https://news.ycombinator.com/item?id=50224100

▲▼ tobiolo20:21 hn>

I think it is a general systems programming language that fits for a variety of use cases but especially well with trees, records and where you want to have direct data (de-)serialization, like flatten out a tree and pick it up then (last one reminds me of FlatBuffers, another invention of the programming language author). I think if you look into the Discord channel there's some nice examples.

▲▼ pulse720:03 hn>

Educate me: how to create a program in Goose which reads a JSON structure, prints it out and finishes. How is memory for JSON allocated on stack? It can be from 1K to 1M - depending on the file...

▲▼ stickfigure20:15 hn>

I don't see why this is problematic. Stack is just memory, you can allocate it by incrementing a pointer. Any amount you want, really.

Apache modules worked like this (still do, presumably). Each request has a stack; allocations bump the stack pointer; when a request is finished, the stack pointer is reset. Incredibly efficient as long as you don't need to keep memory longer than a single request.

Looks like in Goose, each scope acts like an apache "request".

▲▼ abcd_f20:27 hn>

In Windows, by default, thread's stack is capped at 1MB. Main thread gets more but that's somewhat off the point.

─ 2 more ─
▲▼ andsoitis20:18 hn>
>How is memory for JSON allocated on stack?

Check the features section (https://aardappel.github.io/goose/#features), which starts off with how memory works.

▲▼ malisper20:20 hn>

I'm not familiar with Goose, but based on some research and my understanding of how memory works:

At compile time, Goose will analyze your program and determine a number of stacks, N, for it. N is the maximum number of "growable" items you can have in existence at any given point in time. Using virtual memory tricks, each stack can grow up to 2GB, but it uses a contiguous address space and only uses as much physical memory as it actually needs.

For the program you're describing, I think you would end up with two stacks: one to parse the input and a second to print it. When you parse the input, Goose reads characters from the string one at a time and puts them onto the stack. Once it has hit the end of value, it will update the stack so it now represents the decoded JSON value (strings will just leave the characters in place, numbers will pop off the characters and push the number onto the stack). Goose repeats this until it has parsed the whole JSON, and now the whole JSON is on the stack.

Then, when you print the JSON, Goose uses the second stack to build a string. Like the parsing approach, it iterates through each element of the JSON and pushes its string representation onto the stack. Once it's done, it resets the stack pointers to free the memory.

This is really nice because allocating memory is just adding to the pointer, and freeing it is subtracting from the pointer.

In contrast, Rust automatically inserts malloc() and free() calls throughout your code, which are much more expensive than pointer arithmetic. On top of that, freeing a vector of heap-allocated objects is O(n), whereas if done right in Goose, it would be O(1)

▲▼ Aardappel21:15 hn>

https://github.com/aardappel/goose/blob/master/samples/18_js...

Goose can store entire trees (of variable sized things) inside an array.

▲▼ andsoitis20:04 hn>

Lobster is the author's other programming language, previously discussed on HN: https://news.ycombinator.com/item?id=47239955

▲▼ onlyrealcuzzo20:13 hn>

Interesting. Lobster rocks, and Goose is similar to something I've been building on the side, but would rather have someone build for me... Hopefully this guy does it!

▲▼ nvarsj11:56 hn>

Well, it seems Claude mostly built this.

▲▼ vanderZwan20:27 hn>
▲▼ weinzierl20:05 hn>

Rust's appeal (to me) is safety, performance, DX in that order.

Where does Goose sit when classified along these dimensions?

Sure, performance is right there on the packaging but no word about UB in the README. I read "Goose has syntax familiar to C and Rust" as DX in the same ballpark, but is it a performance first or safety first language.

If it is the former it occupies the same crowded niche as C++ and its many cousins and will have a hard time.

If it's the latter it will have much more room to prosper. We don't have enough languages in the niche that Rust and Ada fill right now.

▲▼ vanderZwan20:57 hn>

Well, "safety" is a lot of things so you have to be a bit more specific than that about your use-cases. And I can't really answer it either way - I think WvO has to chime in about that himself - but I can point to some things that might give you some idea of what to expect. Here's what WvO wrote a few weeks ago when he shared Goose on twitter:

>Goose: my latest programming language. Safer, faster and less memory than Rust/C++ for algorithms where tightly packed, flat data structures matter.
>It can represent more kinds of data structures as flat, pointer-free bytes than any language I know.

https://xitter.cf/wvo/status/2107979660558766351

So one way to answer might be: how often do you reach for unsafe when using Rust? If you don't I guess Rust is safer. If you do… it gets trickier.

As for UB, I guess you could try looking through the spec and/or the reference? Like, the code is mostly generated by Claude after WvO wrote a specification. If it were anyone else I'd be worried, but given WvO's cv I actually suspect that means the spec leaves basically no ambiguity.

However, Goose compiles to C. Does that by definition mean UB sneaks in, or is there a "UB free" C subset? I don't write C, so I don't really know.

▲▼ weinzierl21:22 hn>

I guess my question is if you can trigger UB in regular Goose and if you can, would it be considered a bug. I realize that this doesn't necessarily have a simple yes or no answer but I still think it'd help to know if Goose is more on the C++ side or the Rust side.

The complete omission of UB in the README could be one of two things. Either that the topic is not important or that it is considered a given and not worth mentioning.

─ 2 more ─
▲▼ jibalt06:22 hn>

Certainly there's a UB free C subset ... everything specified by the C Standard or designated as implementation-defined is UB free by definition.

UB in C potentially destroys the universe so C programmers do their best to avoid it. A backend that generates C code is a lot more easily constrained than arbitrary manual code. While it's conceivable that a C backend could produce UB code, it would be a severe bug and isn't likely. It most certainly isn't "by definition" ... that suggests some serious conceptual error.

▲▼ Aardappel22:29 hn>

Goose has no "unsafe" feature, and no undefined behavior, and is fully memory safe.

It is obviously has a lot of potential to be very fast.

It even does pretty good on DX. There is no borrow checker to fight. Algorithms that need indices in Rust are strongly typed references in Goose. Generally pleasant easy to read and write code.

The biggest thing against DX may be exactly where performance comes from: you need to think a bit on where to store your data :)

▲▼ groundzeros201502:46 hn>
>and no undefined behavior

Where is the behavior specified?

─ 2 more ─
▲▼ adastra2222:47 hn>

Goose is written by a game developer. They tend to concern themselves with C++'s niche.

▲▼ baalimago20:09 hn>

Do android dream of goose?

▲▼ IshKebab20:28 hn>

PLEASE rewrite this without AI-isms. AI can write perfectly good code now, but its prose - even the latest models - is just awful to read. It hurts my brain to read this.

▲▼ ivanjermakov20:31 hn>

I like it this way, it gives correct expectations regarding effort put into the rest of the project.

▲▼ hbogert17:41 hn>

That's a bit rough. Would we say the same before AI? Bad docs so bad project? I guess people should realize that less is more, don't commit that bad README not even as placeholder.

─ 1 more ─
▲▼ rfgplk20:29 hn>

Keep going, and I hope more people start rolling their own languages.

If anyone is interested, I'm fairly close to finalizing Slang and I'm considering open-sourcing it in the near future. What originally started as a simple register based lang for an embeddable VM turned into an insanely expressive meta?programming language. Full system Fω, expansive whole program reflection (you can reflect on absolutely anything), string memory & logic safety..

You can do stuff like this:

   (fn answer () -> i64 0)
   (defpass build-answer
     (phase source)
     (transform (lambda (f)
       (fn-with f 'body '(let ((a (arr (42)))) (at a 0)))))
     (safety (lambda (goal) (syntax (refl)))))
   (run-pass build-answer answer)
   (fnmut answer
     (transform (lambda (f)
       (if (memory-safe? (check-memory (current-program)))
         f (syntax-error 'unproved-memory)))))
   (defpass inspect-ssa
     (phase ssa)
     (transform (lambda (f)
       (if (memory-safe? (check-memory (ssa-current-program)))
         f (syntax-error 'unproved-ssa-memory))))
     (proof (lambda (goal) (syntax (refl)))))
   (run-pass inspect-ssa answer)
   (answer)
or this:

   (fn next ((i i64)) -> i64 (+ i 1))
   (fncontract next
     (ensures result (successor (Eq i64 result (+ (old i) 1)) auto)))
   (fn read-second ((i i64)) -> i64
     (let ((a (arr (0 42)))) (at a i)))
   (fncontract read-second (requires (one (Eq i64 i 1))))
   (let ((i (next 0)))
     (with-proof ((one (Eq i64 i 1) (result-proof i successor)))
       (print (read-second i))))
it also supports full inline asm or llvm ir:

   (var answer 0)
   (asm (input rdi 20)
        (input rsi 22)
        (output rax answer)
        (mov rdi rax)
        (add rsi rax))
   (print answer)

   (macroop \ (a) (asm (mov a rax)))
   (\ 42)

   (print (llvm add 20 22))
   (print (->i64 (llvm fused-multiply-add (f64) 6.0 7.0 0.0)))
   (llvm fn sum32 ((x i32) (y i32)) -> i32
     (block entry
       (value answer (llvm add x y (declare (flags nsw))))
       (llvm return answer)))
   (print (->i64 (sum32 (the i32 20) (the i32 22))))

Even though you can write this by hand, the core idea is to get a very dense and terse language that LLMs can emit while guaranteeing safety and performance.

▲▼ gspr22:16 hn>
>Even though you can write this by hand, the core idea is to get a very dense and terse language that LLMs can emit while guaranteeing safety and performance.

So now your safety and performance issues are in the LLM and whatever prose you feed it for it to generate slang. Have you gained anything?

(Not meant to detract from the language. It's just that this particular goal seems... strange).

▲▼ anthk23:59 hn>

Try Common Lisp and Coalton. Also, Common Lisp and that one math proof solver.

▲▼ ModernMech02:41 hn>

You should submit your own ShowHN to talk about it rather than hijack some other language’s announcement thread.

▲▼ ebiederm20:29 hn>

I seem to remember the ML guys went down this route. I believe they called it regions.

From what little I read in the literature I seem to recall that there were enough programs with quite a bit more memory usage that they decided this approach was a dead end.

Does anyone know anymore details?

▲▼ Aardappel20:33 hn>

Yes, ML Kit region inference is probably the closest prior art to Goose.

Back then though, they couldn't lean on large 64-bit address spaces like Goose can. They implemented all of this with a chain of blocks of memory instead, and tracked most of it at runtime that Goose now does at compile time.

https://github.com/aardappel/goose/blob/master/docs/design/p...

▲▼ anonymous90821320:34 hn>

[flagged]

▲▼ FrankWilhoit20:34 hn>

The right ideas. Not optimized for reallocation. Creates pressure to overload types, making secure software unattainable.

▲▼ anigbrowl20:35 hn>

This looks very interesting! I mostly write in garbage-collected languages (to the extent that I still write anything by hand...) so I'm just a spectator in memory management debates, but it's often struck me that heap management is more of an art than a science. The idea of treating memory more like stacks made immediate intuitive sense to me. I'm also fascinated by the thread implementation, although I'm not knowledgeable enough to to think through the potential benefits/costs vs other approaches to concurrency.

I wonder if the name of the language was inspired by the way geese fly in formation, with trailing birds saving energy by flying inside the aerodynamic shadow of the leaders, and swapping in and out as needed. If so, a very nice metaphor for the memory management model.

I was also impressed by the documentation. As we know AI-generted docs tends to be rather mediocre, but the author has clearly invested time in making in readable such that the relevance and rationale of design decisions provide the context that a curious programmer needs to understand why things are implemented in a particular way.

I really appreciated the detailed samples, ranging from simple algorithmic theorems up to a small game. Again, I don't have the experience to rate the language vs others, but I was struck by readable the code was and how easy it was to follow task flow. Not having to think about classes is always a plus!

Anyway, I usually the the offering of a new language with dispassionate interest, as something to be aware of and but not get too invested in or familiar with until it gains enough popularity to have a good shot at longevity. Goose is the first language in a long time where I looked at it and immediately thought that I'd like to use it for its own sake.

▲▼ Aardappel21:20 hn>

Thanks!

The name is an animal because all my past PL designs are animals (I also made Lobster etc). Goose seemed like a fun name.

▲▼ malisper20:42 hn>

I'm curious, for someone more familiar with Goose: how do you represent an arbitrary number of dynamically sized values? If you have a fixed number of stacks, it seems like only a fixed number of values can change size at any given time.

For instance, how would you represent a hash table where the values are strings and at random, the program inserts or deletes a random string?

This doesn't seem like something you could easily represent. The only way I can think of is to keep a stack that holds all of your strings, and you periodically compact it.

Edit: Maybe you could also have a separate stack for strings of various sizes. One each for strings less than 8, 16, ... bytes. But at that point you're basically implementing your own malloc by hand.

Edit: Fwiw I do think Goose is very cool! I've had to hand-roll a lot of similar stuff in Rust before to speed up memory management

▲▼ andsoitis20:49 hn>
>how would you represent a hash table where the values are strings and at random, the program inserts or deletes a random string?

Goose has a built-in generic hash table called dictionary<K, V>, provided by its standard library.

https://aardappel.github.io/goose/docs/stdlib.html#dictionar...

▲▼ malisper20:51 hn>

From the link

>Keys and values are fixed-size

so not quite what I'm looking for

─ 3 more ─
▲▼ James_K20:56 hn>

I think the limit of this would be creating your own allocator that works with the stacks, but you could probably stop short of that in most cases.

It's not entirely mad to do it this way, and I can see some benefits. For one, you might have multiple of these pools across the program for different data-types, which would make allocating to any one of them a cheaper operation. It is much more reference safe since you are only ever freeing stuff in a certain context and you can still never have dangling pointers. When you look at memory safe languages, they all default to this eventually. Like Rust in the advanced case will have you implementing your own allocator, lot's of games use this thing called ECS which is essentially a kind of allocator. Goose would just have it be more common.

▲▼ malisper21:08 hn>

This is roughly what I do in Rust. I use different static types for values based on how long they'll live. Temporary values get one type; cached values get another. The allocator allocates each value in a different location based on how long the value will live.

▲▼ progval20:57 hn>

I'm not familiar with Goose, but I'm guessing you could define a single stack and put your dynamically sized values in it. Essentially rewriting a memory allocator on top of Goose's stacks. This is what they seem to be doing here: https://github.com/aardappel/goose/blob/master/samples/13_li...

▲▼ malisper21:02 hn>

Yeah, that looks like a freelist approach. That will work as long as the values you're allocating are all fixed-size. You'll need something much more complicated if you want arbitrarily sized values

That example implements a linked list of integers. I'm curious how you would do a linked list of strings

─ 1 more ─
▲▼ Aardappel21:12 hn>

Certainly, this is a language where you get the most benefits if you are able to structure your data usage such that things of similar lifetimes sit together in arrays.

For example, you can build up a tree of all variable sized nodes inside a single array.

Where it gets interesting is if you must arbitrarily delete tree nodes, and you can't structure it to do that in stack order. The language has various tools for this, like fixed arrays with capacity, or pools of either single elements or runs of elements.

▲▼ 11223305:41 hn>

Does it have (or can it in principle support) "parallel" pools? So that structs can be stored in "columnar" fashion, yet accessed without knowing the particular way they are sliced?

▲▼ SkiFire1314:41 hn>

I was looking into this as well, it seems that Goose does not support having a dynamic number of resizable values, but it does support having a dynamic number of dynamically sized value.

In Rust parlance this would be the difference between a `Vec<Vec<T>>` (many `Vec`s, each can be resized) and a `Vec<Box<[T]>>` (many `Box`ed slices, each with a dynamic size, but none of them can be resized).

I bet you can exploit dynamically sized values to rebuild resizable values, with the downside however that you'll likely leak their memory until you exit the current scope.

▲▼ rvz20:43 hn>

If you can't be bothered to read and structure your own README after getting an AI agent to write more than 10,000+ lines on a single page, it becomes a DONTREADME.md.

Yet, the author expects YOU to read all of what they didn't write. So I ain't reading all of that.

▲▼ pron20:46 hn>

Interesting, but can you write something like an LRU cache?

▲▼ pron22:49 hn>

Ah, I see how that works. Thank you. So I guess the main tradeoff is giving up concurrent data structures.

─ 1 more ─
▲▼ timcobb20:46 hn>
>One memory model: stacks, and scope exit is the free

What are the ergonomics of this I wonder.

▲▼ vanderZwan23:44 hn>

Well, my dayjob is writing JavaScript and I've having a blast writing Goose code by hand in my spare time in the last weeks, so it can't be that bad.

▲▼ skrebbel20:48 hn>

I love that the logo is a very fast goose

▲▼ daoxid20:50 hn>

I'm curious, when returning a dynamically sized array from a function, does the whole array need to be copied? C++/Rust would simple move the array out of the function (i.e., they would only copy the pointer to the underlying data), but it seems this wouldn't work in Goose if the stack the dynamic array lives in is cleared on return?

▲▼ malisper20:59 hn>

Goose determines where the array will be copied to at compile time and allocates it there.

Rust does a similar thing. When a function returns a value, a location for it will be put in the callers stack frame. That means it automatically stack-allocates values when feasible. The downside in Rust is you need to know the size of the value at compile time whereas Goose can do it for arbitrarily sized values

▲▼ Aardappel21:07 hn>

I made the language, AMA :)

▲▼ onlyrealcuzzo21:23 hn>

If you mentioned it in the main page, I didn't see it.

Something that I'm interested in: How fast is the compilation speed on large programs compared to Zig, C, Go?

My ideal language is safer than Rust (no deadlocks / livelocks, no stack overflow from recursion), is optimized for cache locality, but compiles faster than Zig.

This seems like it's close to 2/3.

▲▼ Aardappel22:00 hn>

The current implementation goes thru an optimizing C compiler as backend for "shipping/production" builds, which will limit how fast we can make it compile.

That said, during development/iteration/testing, you can just the built-in JIT, which is based on libtcc (which is very fast), as don't as you don't mind a ~3x runtime slowdown.

▲▼ briandw21:25 hn>

Fantastic project BTW! How much AI help did you have with this project? I'm assuming some. If so is it something that you would have taken on before AI help?

▲▼ loufe22:02 hn>

There's a section at the bottom called "History & Use of AI". Please do your part to keep the discussion here honest and healthy, if not a read of the post, at least a ctrl-f would have been sage.

▲▼ jnpnj21:44 hn>

Was it your first programming language ?

ps: Ah well, I could have searched before asking https://strlen.com/programming-languages/

super neat history !

▲▼ Aardappel21:57 hn>

Haha yeah no, I'm afraid I keep making new ones! :P

▲▼ pixelesque21:58 hn>

Without wanting to be snarky, are the "hand optimized" C++ benchmark files mentioned on the benchmark page available anywhere, as the ones in the "bench/cpp/" directory don't look that optimized to me?

e.g. I see a general lack of reserve()s on strings and vectors (a few tests do use them a bit, like `graph` and `particles`), and there are several other things which I wouldn't personally call "optimized", so I assume those ones are the 'idiomatic' ones?

Same goes for the Rust code: a lot of the code seems to be using Vec::new() and String::new(), instead of ::with_capacity() which would optimize the memory re-allocation (reducing it as the containers/strings grow).

▲▼ Aardappel22:17 hn>

Yes indeed, the real hand optimized files are private, the ones in the repo are decoys.

─ 2 more ─
▲▼ dash222:02 hn>

Do you think it would be worth writing the landing page yourself? The concepts seem interesting but it's painful to wade through stuff like "One memory model: stacks, and scope exit is the free".

▲▼ Aardappel22:21 hn>

I've already edited it plenty, but I guess it doesn't bother me that much.

I preferred people being able to play with it rather than worrying about any claude-isms :)

─ 1 more ─
▲▼ gigatexal21:11 hn>

Would be interesting to see Luke’s Zed language from the replies on HN.

▲▼ kadw21:12 hn>

It would be really great to see how it fits in embedded systems, MCUs in particular!

Embassy (https://github.com/embassy-rs/embassy) uses this concept of no heap allocation and I find it really solid. Even if you write in C, most of the times you don't use heap. MISRA, the C coding standard for automotives, doesn't allow dynamic allocation!

▲▼ Aardappel21:13 hn>

The language as it stands actually uses relatively little physical/committed memory, so could be used in embedded systems, but does rely on there being a 64-bit address space, which is probably still not as common in embedded.

▲▼ malkia00:20 hn>

I was going to ask the same, but I guess it may also not be good fit for some kernel programming, given that the heap is shared along with other code.

But sounds great for 64-bit user space, with enough (48-bit, or more) virtual addressing available. Obviously one can hit some snags if for some reason quite large allocations has to be done, but maybe one has to rethink that too.

I'm starting to fall in love with this approach ;)

▲▼ pradn21:25 hn>

Goose is pretty cool. The idea is that you don’t need to make representable all programs: a language for a restricted purpose is ok. As long as you can statically specify the number of growable memory regions, you can use Goose. However, since there’s no garbage collection, you’ll probably need to be careful about memory use while a program is running. Ie: the stacks can get too big if you keep growing them, even as old data is not used any more.

Still, for a large number of programs - like data-oriented applications - it’s probably a good fit. It might work ok with entity-component game systems as long as you reuse old memory…

▲▼ Aardappel21:53 hn>

You typically "allocate only" if a data structure is made entirely on construction, or if its growth is bounded.

If it is potentially completely unbounded, you need different solutions like a pool, or essentially reconstructing etc.

▲▼ cornholio16:54 hn>

Why not build in a very simple garbage collector - for example a ref counter - and default to it when you can't prove stack tricks are suficient at compile time? This guarantees all programs can be represented and it removes range anxiety for anyone trying a new language.

Seems any compiled language with automatic memory can make use of the techniques to obtain speed ups.

▲▼ pradn02:43 hn>

IIRC, this is feasible. But I think there's a nice simplicity in constraints. You know what you'd use Goose for, if it limits what you can express.

▲▼ groundzeros201521:41 hn>

He didn’t comment on goose. He talked about a concept and the goose guy said “that’s like my idea”. And John said “yeah sounds like it”.

▲▼ Aardappel21:54 hn>

You can see from his comment that he apparently went to the Goose repo and read it. What he references is neither in his original tweet nor my reply.

▲▼ groundzeros201522:06 hn>

The headline is misleading. He hasn’t been commenting about the Goose language and his experience with it. And you are promoting.

─ 1 more ─
▲▼ calvinmorrison22:16 hn>

Can someone explain the weird phrasing here "Nothing moves, so references survive growth" that AI does.

▲▼ Aardappel22:31 hn>

Almost all languages rely on reallocation when a growable array grows, which means you can't store pointers to elements, as they get invalidated upon a reallocation.

In Goose, things grow in-place only, since they have a large amount of address space reserved.

▲▼ calvinmorrison22:36 hn>

no i mean literally the sentence structure

─ 1 more ─
▲▼ malkia23:07 hn>

"Threads that share nothing. A worker is compiled as a separate program with its own memory, and flat values cross typed queues as a memcpy. Data races, locks, atomics and memory orderings do not exist in the language."

That reminds me of Dart Isolates - https://dart.dev/language/isolates

▲▼ wewewedxfgdf23:21 hn>

What are the down sides? That's the key question when a better thing comes along.

▲▼ Aardappel23:23 hn>
▲▼ wewewedxfgdf23:35 hn>

I'd put the pros and cons right up front on the README.

Selling the positives for a new programming languages should go hand in hand with a realistic thorough analysis of the negatives.

Negatives of the language as a brief point 18 on the tutorial makes the negatives look hidden.

─ 2 more ─
▲▼ spopejoy06:24 hn>
>No function pointers

As in nothing functional at all? Or are there anonymous functions etc?

─ 1 more ─
▲▼ cardanome23:58 hn>

I hate when I see a new programming language, get my hopes up and it turns out to be AI slop again.

There might be some decent design ideas there but if you can't be arsed to write them down in your own words but instead make me read AI generated slop I am out.

▲▼ Panzerschrek05:04 hn>

Look at my language: https://github.com/Panzerschrek/U-00DC-Sprache/. It has plenty of interesting ideas, but is fully human-written (and will remain human-written).

▲▼ cardanome16:07 hn>

Oh, I love that!

Mandatory initialization, strong typing, context free grammar, lots of good basics. If you get the C++ header converter to work so you can use most C++ libraries I could see this as a great C++ replacement.

▲▼ malkia00:26 hn>

For Windows, these N data stacks would probably just RESERVE the address space, and as it grows COMMIT it, right?

I guess there would be some penalty to be paid here for smaller pages, but being mostly contigous might also help some.

That to be said, such language seems to require 64-bit virtual address space in order to function reasonably, right?

Also a .dll written in it might not be the best suited, for example if loaded in the middle of fragmented virtual address space it may need to find itself hard to allocate N contigious data stacks (do they absolutely have to be contiguous, what if they are simply ropes or deques or connected chunks ... obviously if some data structure requires to be contigous that would be a problem, but maybe this can be expressed somehow.... or maybe I'm overthinking here).

Super excited about this!

▲▼ Aardappel06:04 hn>

Yup that's how it works. Goose runs great on Windows. Yes, it be very tight to make work on 32-bit. DLLs definitely possible, most Goose programs only use a fraction of the 64-bit address space.

▲▼ malkia00:30 hn>

How would this work in wasm land? It's a bit badly put question, because this leaves out a lot of context - wasm - where, how, what implementation, but from my simple understanding most of the wasm is 32-bit running in a browser. For this to work, it'll need true virtual 64-bit (or 48-bit) address space. The host, being the browser, or something else, may provide it, but....

If the host needs to load several of these .wasm, compiled with goose, then it'll have to run them in separate (browser) instances too (or maybe that's how wasm works by default - not sure).

It just got me thinking - the languge is prepared to translate into javascript workers, because it's really single threaded by nature with communication between threads (or put in the dart languages term "isolates"), which makes them perfect for the browser, now the only question is the memory layout.

▲▼ creata01:57 hn>

I don't know if it's applicable here, but Wasm programs can access multiple memories.

▲▼ Aardappel06:02 hn>

Sadly these memories are accessed thru a constant in the load/store instructions, and Goose would need this to be dynamic (since you can have code that processes pointers coming from multiple different stacks).

▲▼ Aardappel06:01 hn>

To run well, it doesn't just need wasm64 (which I worked on btw!) but it also needs the wasm64 memory to not commit until touched, or have a feature that allows Goose to do that. That and larger total memory. I pushed for that a while ago but I don't think it is possible today.

▲▼ cartothug01:36 hn>

God the AI slop writing is horrible. Please just write the docs yourself, as others have stated, I can’t trust this at all. The “the one idea” made me cringe so hard I almost fell out of my seat. Every time I make something that’s exactly what Claude writes for the starting page.

▲▼ RetardedBastard01:39 hn>

Have you considered rewriting your unintelligible slop comments with AI ?

▲▼ cartothug01:52 hn>

My guy, I ripped this message off on my phone on the train. You should feel happy it’s from a human these days. I am genuinely happy when I read a comment that I can tell is an honest reaction from a person, but not everyone has to feel the same way, I guess. Have fun reading through this documentation tho

─ 1 more ─
▲▼ salah725202:17 hn>

LOL

▲▼ asinglesprinkle03:09 hn>

Im not gonna judge it by anything other than name. Its awesome, Goose it up.

▲▼ alfiedotwtf03:39 hn>

I remember Intel’s segmented memory model, and in the end everyone just wanted a single large and flat segment

▲▼ baud960004:11 hn>

Great work on Goose, a whole load of practical thought went into it!

I’d love to see 1st class lightweight ‘processes’ and typed comms, like CSP. These light processes would be cheap to spawn in the thousands, with synchronous comms. So it’s possible to quickly use arbitrary networks rather than OS thread worker pools with queues. Maybe like:

fn produce(out: chan<i32>) { for i in 1000 { send(out, i); } }

fn consume(inp: chan<i32>) { for i in 1000 { print(recv(inp)); } }

fn main() { let c = chan<i32>();

  par {
    produce(c);
    consume(c);
  }
}

▲▼ Aardappel05:55 hn>

Have you seen the way Goose does threads? It's actually pretty close to what you're talking about: https://aardappel.github.io/goose/docs/reference.html#22-thr...

▲▼ Panzerschrek05:02 hn>
>Memory is a handful of data stacks that the compiler assigns statically

I doubt this can work well in all cases. Sooner or later one needs proper heap.

▲▼ nnevatie07:00 hn>
>and scope exit is the free

And doc is the slop.

▲▼ hbogert07:29 hn>

mighty interesting, i scanned the nuances about the memory model, but it doesn't explicitly say so, but am I right that some workloads will result in heavy fragmented physical memory allocations?

▲▼ Aardappel20:16 hn>

No, I don't see how that would be. It will map process address space to physical memory just like other programs. Physical memory is already not guaranteed to look anything like your process address space, and that is not a problem (for caches, or anything else).

▲▼ stellalo08:16 hn>

I’m wondering: could one achieve the same level of ergonomicity (lack of lifetimes, Rc, …) by imposing a similar “discipline” with thinking ahead about data storage, and staying within a subset of Rust?

Edit: I guess one would still miss Goose’s memory layout, which is (if I understand correctly) where it wins in performance.

▲▼ whacko_quacko08:18 hn>

Just the name made me think it would be an esoteric language with instructions encoded as "DUCK DUCK ... GOOSE"

▲▼ avadodin09:07 hn>

Sources familiar with the matter speculate that the DuckDuckGo browser is going to be rewritten in the language that likely already powers their search backend.

▲▼ hackrmn08:32 hn>

This may be a dumb question, but can't one implement the "N data stacks" in C and thus basically get the principal feature of Goose while staying with C syntax and paying with verbosity?

▲▼ andypants10:48 hn>

Goose compiles to C, so yes

▲▼ vrotaru11:30 hn>

Since Goose, compile to C, I guess the answer is yes, but..

With the but part being the really interesting one.

▲▼ wegwerper09:02 hn>

I get a headache reading this Readme. So much llm slop. Is the project itself a vibe as well?

I find it hard to invest any time in learning about a project if the surface symbols indicate it being a low effort project.

▲▼ forgotpwd1610:31 hn>

It's less being slop and more being technical bomb drop, especially the features section. (And this section pales in comparison to the implementation notes.) Although Claude-generated, it reads fine to programming language nerds like the author but, indeed, will give headaches to those that aren't.

▲▼ amenhotep14:49 hn>

No, it really doesn't read fine. None of this shit reads fine.

▲▼ twiss09:17 hn>
>Returns across multiple calls. `return err from load` returns from a function any number of frames up, statically checked, with no unwinder, no `Result` type and no `?` on every call.

Does this mean you have to know the name of the function calling you that's supposed to handle the error? What if you're a library and don't know that?

▲▼ vanderZwan14:48 hn>

There was some discussion about this in the Goose discord a few days ago.

One idea that came up was this: you could define a particuda function name as the "landing pad" for handling particular error kinds. Then you "wrap" calls to the library in a function with this name. Here's a part from a larger worked example:

    fn catch_io<F: fn()>() -> IoError {
      F();
      return IoError{ message: "", ok: true }
    }

    let err = catch_io() { c(); };
https://gist.github.com/bkietz/9bf510299ab62bb4c7f7d2db6eff0...

Mind you, this was just discussion, no experience with how well this would work in practice.

▲▼ twiss18:19 hn>

Wouldn't it be cooler if you could `return IoError{} from ^` and it would find the closest function in the call stack that can return an IoError?

Even better, you could give it some syntactic sugar like `throw IoError{}`? ;)

P.S. I think the `IoError { message, ok }` type is suspiciously close to `Result<(), String>`.. And if you want to return a value in the success case too it'll become even closer. Perhaps it's best to lean into that and find the calling function that can return an IoError as one of the possible values?

▲▼ amai09:29 hn>

Wow, from the developer of Amiga E: https://strlen.com/amiga-e/

This was my first programming language. Good old times!

▲▼ Aardappel20:10 hn>

Haha small world :)

▲▼ swiftcoder11:25 hn>

This is fascinating, and I'm excited to see an entirely different approach in this space. It looks like the learning curve is going to be a little steep, but if it all works, the underlying principle is very elegant.

▲▼ jcmoyer11:33 hn>

The very first point under your tutorial is factually incorrect.

    var words: u8[][>..] = [];
    for i in 5 { words.push(str("word", i)); }
    
    In C++ that is a vector<string>: one heap allocation for the vector,
    one per string, a pointer and a capacity per element, and a walk over
    all of them when it goes out of scope.
The example you've provided uses strings that are eligible for small string optimization, which avoids per-string allocation. All mainstream STL implementations perform this optimization. You can read about several implementations here: https://devblogs.microsoft.com/oldnewthing/20230803-00/?p=10...

▲▼ vanderZwan12:37 hn>

That removes the allocation but keeps the overhead of a pointer, a capacity and a fixed buffer.

And these strings in Goose are 6 bytes, while the ones in C++ are apparently 16 to 24 bytes each.

▲▼ tialaramex14:18 hn>

But equally they could be a Rust cold_string::ColdString which is either 8 bytes or 4 bytes depending on your target. The 4 byte ColdString is just the bytes 0x77 0x6f 0x72 0x64 to spell "word" in ASCII / UTF-8 [for 8 bytes the current ColdString just stores 0xFF in the other four bytes to denote they are not used]

Rust has had more interesting SSO types than C++ perhaps explicitly because it doesn't come with SSO out of the box although maybe also because C++ still isn't sure whether text is UTF-8 encoded even though it is 2026 now.

▲▼ ModernMech12:47 hn>

This is the danger of using AI to write your docs and why everyone is asking the author to update them.

▲▼ Someone13:10 hn>

I think a human writer could easily have made that error, too.

─ 10 more ─
▲▼ felixguendling13:54 hn>

in C++ this could be done with `cista::raw::vecvec<char>` where only string start positions are stored (end is not necessary because end = start of the next -> 4 byte per string) https://github.com/felixguendling/cista/blob/master/include/...

▲▼ mgaunard15:09 hn>

You can also just copy your strings into the same buffer in C++ if you want to.

The obvious downside is that resizing any string but the last requires moving everything down the stack.

With objects that never need resizing, you can of course have much simpler memory layouts.

▲▼ lisp224011:40 hn>

If only I had known back when I was buying a floppy of Wolfenstein 3d in the 90s that the game that lets you shoot Nazis and their German shepherds would directly lead to actual Nazi mutant cyborg war dogs.

https://www.motherjones.com/politics/2026/10/palmer-luckey-a...

Can't wait find out what terrible crimes against humanity Goose will cause.

▲▼ shooshx11:54 hn>

There will never be another new mainstream programming languages.

▲▼ tonis213:20 hn>

Maybe I'm dumb but how can a proper language work without heap and only use stack memory, stack only works if you know the size you want before compiling, or am I wrong ?

▲▼ nlitened20:04 hn>

A stack can grow arbitrarily deep (in practice, assuming 64-bit address space), but it grows only in one dimension. However, you can also have arbitrarily many stacks, so you can have a separate stack for every single thing that can grow in size in your program (by statically analyzing what variables hold data structures that could grow, and how many stacks at most you will need at the peak when program runs).

▲▼ jeff_ciesielski13:23 hn>

Rather than salty comments, I just decided to give it a try.

Ported a couple of algorithms from my quite large commercial rust codebase and they were significantly faster in goose. Was wow'd, but the big gain was actually that goose just led the LLM to use more efficient (though admittedly slightly less rust-ideomatic but still safe) data structures, so porting them back kept nearly all of the gain (and in one case, even more).

The obvious 'no shit' comment is 'well your code just wasn't optimized', which is partially true, there was obviously a fair bit left on the table. What I find more interesting is that for darn near zero cost, having an an LLM explore in different languages led to a better outcome than me sitting here running perf and squeezing for cycles.

I'm sure that as I build up a cookbook for my codebase this will be less useful over time, but for now, this is going to be how I approach first order performance optimization.

Nice work, cool tool, plz de-ai the docs :D

▲▼ sergiotapia14:41 hn>

Rewrote a little pet project of mine from Rust to Goose. From 17MB ram usage to 6MB ram usage. It's not much but it's honest work.

https://nephew-tommy-pranks.up.railway.app

▲▼ saidnooneever15:24 hn>

This looks pretty cool Always wonder about fast low level languages, can you play with its linkage enough to say run it on baremetal x86_64 or so? And how complex is the runtime? is it just transpiled to C? (i can see some notes about spitting out a single C file but wasnt sure)

Either way nice to see more options in this space.

Do you know a bit of an ETA / roadmap for certain features like the dynamically sized moves and struct members?

p.s. love potato and geese so feelin kinda biassed towards being excited about this :D

▲▼ Aardappel20:12 hn>

The runtime is not particularly complex, but making it run with no runtime would still take some doing :)

You'll have to explain what you mean exactly by "dynamically sized moves and struct members".

▲▼ exaroth18:07 hn>

If any Vim/NeoVim users want to play with the language, I made Vim plugin for Goose:

Link: https://github.com/exaroth/goose-lang.vim

▲▼ jkl5xx01:49 hn>

I know caring about syntax is verboten, but Goose checks off a lot of my dream language syntax features. UFCS, implicit it parameter, trailing block lambdas (with curly braces!), algebraic data types, exhaustive enum matching, pattern matching, etc... the list goes on! It almost looks like Kotlin but more functional b/c it doesn't have the JVM OOP baggage (I'm a big Kotlin fan). And an interesting, fast memory management paradigm to boot! Honestly, even with a garbage collector, this language looks so fun to write and read.