Btw I made the language, AMA. repo: https://github.com/aardappel/goose
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.
>>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
That's like me with violins!
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:
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.
Neither can Gemini.
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?
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.
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.
It was not well documented. The significance of using "data stacks that the compiler assigns statically" was NOT clear until I backed out of the page and started reading the HN comments here.
Why are low effort comments not worthwhile but low effort docs are fine?
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.
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.
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.
These AI generated docs would make more sense if you instruct it to not include numbers unless they form a complete example.
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.
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".
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.
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.
Great work on Goose, a while 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 thousands, with synchronous comms. So itās possible to quickly use arbitrary networks rather than OS thread worker pools with queues. Maybe like:
fn producer(out: chan<i32>) { for i in 10 { send(out, i); } }
fn consumer(inp: chan<i32>) { for i in 10 { print(recv(inp)); } }
fn main() { let c = chan<i32>();
par {
producer(c);
consumer(c);
}
}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
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.
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...
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
> 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...
From the link
> Keys and values are fixed-size
so not quite what I'm looking for
You can store the strings separately and have the dictionary hold references to them.
Conceptually:
dictionary<i64, StringReference>
|
+----> "hello"
+----> "a much longer string"
+----> "x"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.
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.
"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
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ā¦
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.
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?
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...
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.
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.
"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
Get a daily email with the the top stories from Hacker News. No spam, unsubscribe at any time.