Top
Best
New

Posted by bobbydigitales 13 hours ago

The Goose Programming Language(aardappel.github.io)
https://twitter.com/ID_AA_Carmack/status/2113704269274636568
364 points | 240 comments
Aardappel 12 hours ago|
Btw I made the language, AMA. repo: https://github.com/aardappel/goose
adastra22 9 hours ago||
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.
assimpleaspossi 7 hours ago|||
>>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

lilbigdoot 3 hours ago|||
Right I feel like they just got to a point where they can reliably write individual functions with direction. Every time I've tried to use them consistently I feel like I just end up spinning wheels from them doing things that mostly work until it crashes and burns or just has straight up missing things or half truths. Skill issue or not it doesn't work for me.

Still glad I basically never have to debug a build issue or memory error again though

avadodin 1 hour ago||||
If the Code is bad, you can tell it to fix it to match your expectations —only at the edges, are you going to have to spell it out.

Lack of documentation used to be a running joke.

The goose_spec.txt in 2019 would be "goose is like c and rust only with syntax like array{;}[] btw it uses a small amount of stacks instead of malloc."0 10klocs.

Now, we make sure the AI leaves documentation so it can figure out what it was doing earlier.

A local model a few generations and many weights behind newest Claude can implement my novel algorithms and write the documentation I wouldn't.

Yes, many times it does dumb CS for kindergarten mistakes but it only has to work towards the optimum every once in a while.

hexasquid 6 hours ago||||
That's like me with violins!
b33l 6 hours ago|||
Maybe violins don't write good code after all?
slopinthebag 1 hour ago||||
it's definitely true in real life. i think there is actually a massive skill gap in using them effectively.
incrudible 3 hours ago|||
I can see that you only work with true scotsmen, but the machine really does write most of the code now, at least from a pure numbers perspective.
dirkt 2 hours ago||||
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).

ckx 1 hour ago||
This is actually a really good point that I think many people fail to understand these days!
WillAdams 8 hours ago||||
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

polski-g 7 hours ago||||
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.
adastra22 4 hours ago||
Neither can Gemini.
felixgallo 9 hours ago|||
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?
hackrmn 44 seconds ago|||
Comprehensibility does not equal correctness or fitness. As if that wasn't enough, or perhaps in most related manner, an LLM/AI cannot be held responsible for what it produces, by nature, especially considering the fact that a _human_ claims all of it as their own work, or at least take responsibility. Meaning that when a parable of documentation is discovered to be bogus, you can't pin it on AI, you go to the human and ask them why the documentation is wrong, and it's not a good look.
spuz 8 hours ago||||
If you've ever been tasked with coming up to speed on a system mostly documented with AI writing, you'll quickly realise how hard it is to trust anything you are reading. Even if the Claude documentation is 100% accurate, you'll never shake the worry that some crucial fact you founded your understanding on was wrong. It's a fast recipe for psychosis.

That's without even mentioning that AI writing can often state the what but not the why. That human perspective is usually the most valuable knowledge to get across in writing.

vikramkr 1 hour ago|||
at that point i just ask an ai questions about how the system works and hope the reasons for particular decisions can be reverse engineered from git history/pr comments etc.
JojoFatsani 8 hours ago|||
There’s no reason to trust a human any more than an LLM for this. Humans are not 100% accurate and very capable of making crucial factual errors in documentation. Ask me how I know.
foltik 7 hours ago|||
There’s a _very_ good reason. Humans have a much richer mental mental model of the codebase, design decisions, external factors, etc.

Of course Humans can make errors when translating that to English. But if you’ve read any amount of Claudish it should be obvious that interpolating the code to documentation with an LLM typically produces a significantly worse result in almost every way. Except for superficially “sounding” correct.

brabel 1 hour ago|||
That’s just not true especially when the author designed the language with the LLM like he says in the end (the AI is very good at forcing you to be specific and make decisions on ambiguous terms): all decisions can be included in the repository in a way the computer can follow and understand, which is a pattern called specification graphs. You can just document things in the docs as well if you want a more basic method. If you are claiming this language is worse in every way than others that were made by humans alone, could you be specific?? What are the bad things you’ve found? What part of the docs are incorrect? Or are you just commenting without having looked at any of that?
vikramkr 52 minutes ago|||
That is not necessarily true if the codebase is written by AI unless the human chose to be involved. That was also not necessarily true before AI either. Human skill level is wildly variable
creata 5 hours ago||||
It's probably possible to prompt an LLM into writing well, but most LLM writing that I've seen is poorly structured (e.g., the paragraphs in this post feel like strings of punchy marketing phrases), and the LLMs are too good at sounding like they're saying a lot while saying very little.

Plus, if a human puts the effort into writing something, that signals to me that it's worth something, because human time is much more scarce than LLM time.

ashkankiani 5 hours ago|||
I don't want to read software written by misanthropes. If you've given up on working with other people, then what is the point exactly?
adastra22 9 hours ago||||
As I said in my message, it would determine whether I use the project or not. I need to match it to my use cases, and if I find a match I need to know: is it matching because my use case falls in the domain of what the human collaborator wants from this project, or is my use case a hallucination that will go unsupported?

You cannot tell either from the AI written docs. I'd rather OP gave a rambling post about some problem he has in gamedev that led him to think a new language was needed, why the constraints are different, and how this will improve X, Y, or Z in his own words. Then I can infer what this project is and where it is going. Right now, I have no clue.

knowsuchagency 9 hours ago|||
The docs are perfectly legible and to the point. I don't see the need to waste the author's time with aesthetics
adastra22 8 hours ago||
Legibility is not the concern. The issue is whether the point the docs are making are the actual point the author is concerned with.
vikramkr 56 minutes ago||||
What's left to determine? Obviously AI written docs are a deal breaker for you. Seems very straightforward. If docs are ai written, move on. That's an immediate and obvious fundamental incompatibility you have with the project. Why are you still complaining about the docs being ai written getting in the way of making your decision when you've clearly already made it?
felixgallo 9 hours ago||||
With respect, what on earth are you talking about? The documentation is clear about what the language does. You can download it and test it out if you like, and verify if yourself if the documentation matches the feature descriptions. What the human collaborator wants is immaterial. You get the code.

The author has written that post about why the language came about. He doesn't owe you that, but he did it anyway. He doesn't owe you anything more. You can use it or not. Advising him to throw away all the documentation and rewrite it again by hand from scratch so that adastra22 can deign to examine it further is ridiculous.

If you have a question about whether or not this further iterations will fit your specific (and so far undescribed) particular use case, the correct avenue for that is to ask the author politely. Or Fable; Fable could probably tell you without having to bother the author. But holy cow, the entitlement.

adastra22 8 hours ago||
Are we now in a situation where all code is disposable, throwaway things with no long-lived projects?

We're talking about a programming language. Languages, and their compilers, evolve over time. Where is this one headed?

baq 35 minutes ago|||
with programming languages which just rehash ideas (not saying it's a bad thing mind you! a good mix is better than a sum of its parts) you can nowadays get literally years of work in a week. truly novel stuff like bend2 takes longer (ask Victor), but I expect the next generation of models to compress those timelines, too (if the next generation is allowed to be released, since the security brouhaha going on now is seriously concerning)
vikramkr 59 minutes ago||||
pretty much that's the default assumption yeah. Anything new is probably a throwaway. If there's anything cool in it - rebuild it to your own taste. There will be exceptions that do become long lived projects of course. probably even the same amount there were before. But there's going to be a few orders of magnitude more throwaways so on average any new piece of code is going to be a throwaway
felixgallo 6 hours ago|||
If you think you'll be able to divine that if and only if the documentation is entirely written by a human, then I'm afraid I'll have to disappoint you: no language has ever evolved strictly according to the initial plan of their authors. Nor has any author's strongly held belief about the future ever panned out the way they thought. Nor has the documentation ever held the immutable future of the language, even when fully human generated (which essentially zero languages have, even before AI).

The way to find out if a language is suitable for your needs is not to demand, in an entitled way, that the author perform an act for you. It is to (a) ask the author politely; or, failing that, (b) to examine it yourself (or ask Fable to evaluate it, using tokens you paid for).

This particular language, further, is Apache 2.0 licensed. If it's not working for you, fix it. Jesus.

jibalt 3 hours ago||
Your correspondent keeps moving the goalposts and never presents a coherent argument. I would just leave them be.
adastra22 2 hours ago||
How have I been inconsistent?
hexasquid 7 hours ago|||
Seems like an AI could be tasked with verifying the docs. Then a human can double check it's work if it finds anything.
adastra22 7 hours ago||
Verify against what? What I want verification against lives in the human's head, not the repo.
hexasquid 6 hours ago||
Oh, I was thinking verify what the doc claims to what the code actually does, I misunderstood you.
condwanaland 9 hours ago||||
Why are low effort comments not worthwhile but low effort docs are fine?
nozzlegear 4 hours ago||||
> What action would you take differently if a given feature were implemented by Claude, or a human?

This one is easy: I'd read the docs if it were written by a human, but I won't read them if they're written by Claude.

fuzzythinker 6 hours ago|||
I find your comment much much lower effort than GP you took a snip at. It's true that more ppl may align in your thinking, but dissing the other side's concern as low effort does not belong in hn.
emmelaich 3 hours ago|||
And ... you made Lobster lang, Cube2/Sauerbraten 3d shooter and voxray/voxile and lots of other cool things.

https://strlen.com/

vermilingua 1 hour ago|||
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.
tymscar 2 hours ago|||
Sauerbraten is such an amazing game. Every so often I boot it up and play some venice instafrag
koolba 12 hours ago|||
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.

Aardappel 11 hours ago|||
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.

eslaught 11 hours ago|||
As far as I can tell, you're essentially creating a new stack for every dynamically-sized variable (because otherwise nothing would be able to grow by bump allocation without moving). But this means you're creating a separate virtual memory allocation for each of these objects, which (I presume?) then rounds the size up to the OS's minimum page size (4KiB or similar).

Doesn't that waste a lot of space? Or is there something I'm missing?

Also it would seem to force every allocation to go through the OS kernel to allocate unless there's some trick to avoid that.

vanderZwan 11 hours ago|||
I think what you might be missing is that you can also do a sort of livenes analysis to determine how many dynamically-sized variables are "awake" at the same time at most. Then you only need to create as many stacks as that number, because you can reuse the stacks of "sleeping" variables (since the "awake" variables are guaranteed to be freed once their block goes out of scope, and threads don't share any memory so no data race worries regarding those stacks).

For example, suppose we have a function with a growable array that calls another function (without passing said array) that also uses a growable array as some temporary scratch space. Then the first function's array isn't being used when the second is sleeping, so the inner function's array could safely share the stack because it will all be freed with a single pointer restore once the second function exits.

Now, I actually don't know how the compiler works but as I understand it does things similar to my example to optimize the number of stacks needed (but let's wait for WvO to confirm or deny whether my understanding matches reality).

Xirdus 6 hours ago||
The obvious question is how does it work for linked lists, trees, and other non-stack-shaped data structures?
cardboardguru 3 hours ago||
At the end of the day trees are still stored linearly in memory. Think of it as an array, with each node an element of the array, and indexes used rather than pointers.

Plenty of trees only ever grow. And that's the normal type of array in Goose. However you can also have arrays that can grow and shrink, if you have a tree that is constantly in flux.

Either way it'll be one stack for an entire tree.

Aardappel 11 hours ago||||
Only top level growable arrays get their own "stack", if you create an array of arrays it only uses one stack.

How you deal with the inner stack growing depends, ideally they don't grow (construction only), or you can make them fixed size, or with a capacity, or in the worst case, as a pool. The language has facilities for all of these.

amluto 9 hours ago||
What’s the “pool” case? A function allocates a growable arena in normal heap or virtual memory?
Aardappel 9 hours ago||
https://aardappel.github.io/goose/docs/reference.html#14-reu...
cardboardguru 3 hours ago|||
All it's doing is allocating address space. And 64 bits means you have an astronomical amount of that. It only needs to become actual memory when you use it. A page at a time.
koolba 11 hours ago||||
These AI generated docs would make more sense if you instruct it to not include numbers unless they form a complete example.
randfur 8 hours ago|||
I wish it was more clear about the memory layout of nested variable sized data structures. As far as I can work out the max capacity is decided at construction time which makes the whole thing a lot less intriguing.
lynndotpy 11 hours ago|||
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".

nextaccountic 11 hours ago|||
> 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?

Aardappel 11 hours ago|||
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

nextaccountic 1 hour ago|||
> 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.

Well message passing with literal memcpys (rather than, say, just a shared buffer, like is common in Rust) is very expensive when the messages are huge. Of course as the message size increases, the Rust program will become faster than the Goose one (and if it doesn't, you should look into your benchmarks)

Being more specific, it means that for each message, we are writing on memory twice - one to build our own buffer with the message, and another to make a copy to another worker. Message passing violates, in spirit, the Goose rule that things are built on their end destination: we can't write into a buffer owned by another task, so we need to first write on our own buffer, and then do a copy, which is a lot like first writing the value to the stack, then copying the heap. Writing twice has an overhead, specially when messages don't fit in the cache - you just need benchmarks with sufficiently large messages to demonstrate it.

Beyond that, there are some algorithms that can be made much faster when written with atomics rather than message passing. (Also there are algorithms that will get faster if you use locks! Locks aren't always slower). It's just a matter of finding such examples, and writing a Rust code that will use atomics, compared to a Goose program that uses messages between workers because Goose doesn't expose atomics to the user.

So if you didn't find such examples, it's because you didn't specifically write benchmarks with those scenarios in mind (or, I mean, didn't told your LLM to write this).

That is: if the Rust program and the Goose program do the same thing in the same way, then it's reasonable that Goose is faster on average. But Goose is limited because it doesn't let you do some things, but Rust does, so to compare the languages you need to find those cases where the Goose limitations end up making the software slower

Jtarii 8 hours ago|||
That site is just benchmarking how well an AI can implement things, it has nothing to do with how "fast" the language is.
jibalt 3 hours ago||
> It evaluates 27 programming languages across 50 real-world-inspired tasks using 5 metrics: runtime, runtime score, memory usage, compile time, and expressiveness.
cardboardguru 2 hours ago|||
Sounds like they are processes, as the docs say that all the global data is duplicated for the workers to have a private copy.
ashkankiani 5 hours ago|||
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.

ashkankiani 3 hours ago||
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

cardboardguru 2 hours ago||
Yeah, my language in development uses only arenas for memory management too. But this Goose language is next level.
Zecc 4 hours ago|||
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.
Aardappel 2 hours ago||
Hah cool, small world :)
JurassicTyler 5 hours ago|||
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?

Aardappel 2 hours ago||
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.

AnEro 12 hours ago|||
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!]
Aardappel 12 hours ago||
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.

AnEro 11 hours ago||
Thanks much appreciated!! Feeling silly for not noticing my best experience with AI in larger/complex cases has been languages/frameworks that are opinionated in usage/design(zig/go/elixir/rust)
SwellJoe 8 hours ago|||
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."
Aardappel 2 hours ago||
Thanks, that brings me back, haha :)
Zecc 4 hours ago|||
You might want to update https://strlen.com/language-design-overview/ at some point.
Aardappel 2 hours ago||
Haha yes, haven't updated my website in a while :(
giovannibonetti 8 hours ago|||
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?
Aardappel 2 hours ago||
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.
avilay 6 hours ago|||
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?
Aardappel 2 hours ago||
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.

avilay 46 minutes ago||
Thanks for your response. I have been wanting to dip my toe in the space of language building for a while, but keep getting discouraged by the advances of coding agents. Love your reason for building this!

> I made this one because I had a really strong design in mind that I think should exist.

az09mugen 3 hours ago|||
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."
jibalt 3 hours ago|||
I don't think it's a mistake. He's claiming that it's faster than C++ and it's faster than safe Rust.
stopping 2 hours ago||
The extra "than" in front of "safe Rust" is superfluous and reads like an error, as if there's a comparative adjective missing ahead of it. I don't see any situation where "and than" should ever appear in any non-contrived English sentence.
cardboardguru 3 hours ago|||
Why would that be a mistake?
amelius 12 hours ago|||
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?

Aardappel 12 hours ago||
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.

fancyfredbot 12 hours ago|||
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?

Aardappel 12 hours ago||
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.

fancyfredbot 11 hours ago||
Thanks!

I'm a bit confused about how you could decide the number of stacks at compile time. I can see you could see how many arrays are lexically in scope. However if the code puts one dynamic array inside another dynamic array I can get to an unbounded number of total stacks without ever needing more than two in lexical scope at once. Do you also add stacks at runtime? Like a stack of stacks?

Aardappel 11 hours ago||
The top level arrays all are owned by a local variable. An array of arrays is just allocated to a single stack.
fancyfredbot 11 hours ago||
I'm sorry if I'm being dense here but I can't see how that would work?

The spec on your GitHub says: "Two simultaneously-live resizable values never share a stack."

So I surely can't have a dynamic array of dynamic arrays share a single stack? That's an unbounded number of resizeable values sharing a stack?

You could make it work on a single stack but that would involve having non-contiguous memory for each inner array since you can add to any array in any order.

The spec implies that we need one stack per element of the outer dynamic array which would let you have contiguous memory for each inner array, but you wouldn't know how many stacks you needed at compile time.

Aardappel 9 hours ago||
The language has different levels of solutions for the inner arrays being dynamic, for example a) only dynamic upon construction, b) fixed size, c) fixed size with cap, or d) allocated in a pool in a different array.

Depending on your needs you use one of these solutions.

osigurdson 9 hours ago|||
What are some use cases (if any) that the language is not suitable for?
malkia 8 hours ago||
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.
lemming 11 hours ago|||
Do you consider Goose to be a replacement for Lobster, or do they fill different niches?
Aardappel 11 hours ago||
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.

lemming 11 hours ago||
Do you have plans to continue using AI to improve Lobster too, e.g. working on implementation speed, or perhaps self-hosting?

How have you found AI at writing Goose or Lobster code, if you give them a skill or something, are they relatively competent at actually writing code in the language?

Both are really interesting projects, BTW - thank you! I've been meaning to try Lobster out but haven't found time, I think this might be the push I needed.

Aardappel 11 hours ago||
I've already been improving Lobster's speed a lot in recent times: https://github.com/aardappel/lobster/blob/master/tests/compa...

Self-hosting would be cool too, but for both Lobster and Goose I've tried to keep it simple for people to build the language in any environment, which is why its still all in C++.

I don't use any "skills". From what I can see, the #1 way an AI produces "good code" is to simply work in the context of existing good code. I don't need to hold it by a style guide, it seems to infer that from context just fine. At least Fable does, other models less so.

andyferris 9 hours ago|||
I suppose something like a skill would help those starting with the language. If nothing else, a well written skill can be read by a human - or you could load the skill and generate a program you are familiar with.
vanderZwan 11 hours ago|||
> From what I can see, the #1 way an AI produces "good code" is to simply work in the context of existing good code.

"Just Git Gud First" - Wouter van Oortmerssen

perrygeo 8 hours ago||
That's unnecessarily dismissive. I find the statement quite enlightening and I wish other engineers would realize the wisdom here.

LLMs are pattern matching machines. Your code is the base template that will determine all patterns moving forward. No amount of context engineering or prompting can save your code from itself.

Pragmatically, it suggests we need a much greater investment in code quality, and much less hand-wringing over AI tooling.

vanderZwan 2 hours ago||
Apologies, I wasn't trying to be dismissive, it was meant to be a joke that the sentence read like an (earned) humblebrag.

For context: I'm very active on the Goose discord (and have been following WvO's languages since Lobster was announced on Google Groups eons ago). I goof around there too, and forgot for a moment that outside of that chat it wouldn't be obvious that I'm being tongue-in-cheek.

ksec 12 hours ago|||
First, Congrats on the release!

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

Aardappel 12 hours ago||
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.

jdw64 10 hours ago|||
Why was it named Goose?
Aardappel 10 hours ago||
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.
spacemarine1 9 hours ago|||
I feel the need, the need for speed. Talk to me, Goose.
jdw64 8 hours ago|||
Thank you.
vanderZwan 12 hours ago|||
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?)

Aardappel 11 hours ago||
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 :)

groundzeros2015 10 hours ago||
Why are you pretending that John is endorsing your project?
stellalo 10 minutes ago||
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?
whacko_quacko 7 minutes ago||
Just the name made me think it would be an esoteric language with instructions encoded as "DUCK DUCK ... GOOSE"
malisper 11 hours ago||
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

Aardappel 11 hours ago||
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.

112233 2 hours ago||
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?
progval 11 hours ago|||
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...
malisper 11 hours ago||
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

andsoitis 11 hours ago|||
> 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...

malisper 11 hours ago||
From the link

> Keys and values are fixed-size

so not quite what I'm looking for

andsoitis 11 hours ago||
You can store the strings separately and have the dictionary hold references to them.

Conceptually:

dictionary<i64, StringReference>

             |

             +----> "hello"

             +----> "a much longer string"

             +----> "x"
MeetingsBrowser 11 hours ago||
Isn’t this just re-inventing the index arena pattern, which nullifies much of the benefit over rust?

> Goose code holds typed references where the equivalent Rust holds u32 indices into an arena — and it is where a good share of the benchmark wins come from.

andsoitis 8 hours ago||
In this particular case, you could make the values fixed length strings. Somewhere, performance and memory trades.
James_K 11 hours ago||
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.

malisper 11 hours ago||
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.
malkia 9 hours ago||
"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

ebiederm 11 hours ago||
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?

Aardappel 11 hours ago|
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...

pradn 11 hours ago||
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…

Aardappel 10 hours ago|
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.

anigbrowl 11 hours ago||
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.

Aardappel 11 hours ago|
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.

bobbydigitales 12 hours ago||
"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

pulse7 12 hours ago|
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...
malisper 12 hours ago||
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)

Aardappel 11 hours ago|||
https://github.com/aardappel/goose/blob/master/samples/18_js...

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

andsoitis 12 hours ago|||
> 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.

stickfigure 12 hours ago|||
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_f 11 hours ago||
In Windows, by default, thread's stack is capped at 1MB. Main thread gets more but that's somewhat off the point.
Xirdus 4 hours ago|||
There's stack the CPU primitive, and there's stack the heap-allocated data structure. Goose's multiple stacks are the latter. Unrelated to OS-managed thread stacks. More like VM-managed thread stacks in Java and other bytecode languages, which also live on the heap.
stickfigure 11 hours ago|||
Stack size is completely arbitrary, just like heap size. You have 2^64 addresses to work with.
More comments...