77 comments

[ 1.6 ms ] story [ 12.4 ms ] thread
I can't see a reason to use this and not C++ It reads like slop text
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

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 (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.
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...
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".

In Windows, by default, thread's stack is capped at 1MB. Main thread gets more but that's somewhat off the point.
Stack size is completely arbitrary, just like heap size. You have 2^64 addresses to work with.
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.
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 parsed a value, it will update the value on the stack to represent the decoded JSON value. 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)

(comment deleted)
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 front and center 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.

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.

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.

Hmm, well the benchmarks always use an explicit "faster than idiomatic C/C++ and safe Rust". Similarly it also notes that in theory you could write equally fast C/C++ (since Goose compiles to C), but it would be an unreadable, unsafe mess.

Note also the fact that e.g. array indexing has bounds checks without escape hatches for the programmer, but the compiler can elide quite a few of them, and the language has tools to help the programmer write code that is guaranteed to be check-free.

I think that suggests the ambition is to compete with Rust in the domain of combining safety with not compromising on performance.

Goose has no undefined behavior.
Certainly there's a UB free C subset ... everything specified by the C Standard or designated as implementation-defined is UB free by definition.
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 :)

Goose is written by a game developer. They tend to concern themselves with C++'s niche.
(comment deleted)
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) but compiles faster than Zig.

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.
Keep going, and I hope more people start rolling their own languages.
> 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).

Try Common Lisp and Coalton. Also, Common Lisp and that one math proof solver.
You should submit your own ShowHN to talk about it rather than hijack some other language’s announcement thread.
An AI-generated language with an AI-generated article proclaiming AI-generated benchmarks about how it's 3x faster than C++. Spare me.
The right ideas. Not optimized for reallocation. Creates pressure to overload types, making secure software unattainable.
Oh, is this then the Holy Grail of memory handling or what?!

Please share it with logistics guys, since putting stuff from here and there into warehouse storage, then taking away, put some more in than planned sometimes, keeps spinning their heads. They need all the good logic!

So, yet another programming language, that's nice. I never thought there will be one more beyond English.

  Evolution of programming languages:
  - 1940s → Machine Code ( 0s 1s )
  - 1949  → Assembly
  - 1957  → FORTRAN
  - 1959  → COBOL
  - 1964  → BASIC
  - 1970  → Pascal
  - 1972  → C
  - 1983  → C++
  - 1991  → Python
  - 1993  → Ruby
  - 1995  → Java
  - 1995  → JavaScript
  - 1995  → PHP
  - 2000  → C#
  - 2009  → Go
  - 2010  → Rust
  - 2011  → Kotlin
  - 2011  → Elixir
  - 2012  → TypeScript
  - 2014  → Swift
  - 2015  → Solidity
  - 2026  → English
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.

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

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.

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.

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?
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.

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.

Interesting, but can you write something like an LRU cache?
Ah, I see how that works. Thank you. So I guess the main tradeoff is giving up concurrent data structures.
> One memory model: stacks, and scope exit is the free

What are the ergonomics of this I wonder.

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.
I love that the logo is a very fast goose
I'm curious, when returning an 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?
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

I made the language, AMA :)
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.

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.

[delayed]
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.
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).

Yes indeed, the real hand optimized files are private, the ones in the repo are decoys.
(comment deleted)
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".
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 :)

It's easier to play with if the documentation and initial selling page is easy for humans to read!
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!

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 ;)

(comment deleted)