117 karma · joined February 20, 2023
fn foo(x: usize) -> NonZero where x != 5 { NonZero::new(x - 5) }
I do not believe this is achievable in Rust.
let x = vec![1, 2, 3]; let a = &mut x[0]; let b = &mut x[1]; swap(a, b);
Because Rust doesn't really know the difference between the whole Vec an element within the vec. Therefore it treats any mutation as one that could potentially invalidate anything with the same lifetime and doesn't compile. So I believe the plane model to be strictly better because it allows for this and still prevents the real scary thing:
let x = vec![1, 2]; let a = &mut x[0]; x.push(3); a = a + 1;
Your hardware does this OR You implement it in software
Each “where” is called a plane, which is a tree. The stack plane contains all your function calls memory, a structure allocated on the stack has a child plane for each field. The compiler tags which plane something is from at compile time and tracks when it dies.
Things do get complicated in scenarios like:
fn max(a: &usize, b: &usize) -> &usize {
if *a > *b { a } else { b }
}In such a scenario the compiler tags the result with both of the input planes. However, you cannot define add a postcondition ensures clause to make it more precise
let x = vec![1,2,3]; let a = &mut x[0]; let b = &mut x[1]; swap(a, b);
This is not allowed. And actually in the compiler’s mind it’s for the same reason as this:
let x = vec![1,2]; let a = &mut x[0]; x.push(3); a = a + 1;
Which is definitely not safe and should be rejected. But the compiler cannot reason that the above case is actually safe, because they both fail to compile for lifetime reasons.
Another thing I’m working on are “patterns” which are like enums on steroids. In Rust you cannot define how an enum is laid out, thus requiring optimizations for types like Option<&T> to be in the compiler. In evergreen this will be in the language via a similar process to trait implementation.
That is good advice, I’m definitely planning on including some batteries soon, once I’m very happy with where the compiler is at.
Luckily LLMs are quite good at these surgical changes. In fact I only used ~10% of my weekly limit on Codex (granted, I have the $100/mo plan) and a few iterations of review to implement them. I wouldn't go and use them in production in their current state but it's fascinating to see just how easy it is to add these features in a workable manner to projects as large as Go.
A little info on how each of the features work:
1. Tagged unions: These are compiled down to a struct with a series of 0-length arrays of each of the union field types. This enforces proper alignment. There is a byte array which is as long as the longest subfield where the payload lives. There is also an 8, 16, 32, or 64 bit variant field for distinguishing the active variant. Finally there is a "gc anchor" (codex's naming) which is a pointer to information for the gc to properly collect the active field. While this field is technically redundant, implementing unions without it would require a significant change to gc. Ideally I'd like to keep this fork (if I choose to maintain it) more or less in sync with master so I've decided not to do this for now.
2. Early return with "!": In a multi-value context if the final value is of type error I can use the special "!" bang operator. I can only use this operator if the current function I'm in has its final return type as an error. If both of these condition are met the bang operator will early-return from the surrounding function if the final value of the multi-value context is a non-nil error. This reduces a lot of "if err != nil" boilerplate.
Finally I asked the LLM to refactor the go source to use these two new constructs on this branch (https://github.com/jordan-bonecutter/go-plus-plus/pull/3). I haven't looked much at this branch myself but it easily (within about an hour) removed 5k+ lines of code from the go source. It also found some real bugs, like a potentially nil error value being returned in some plan9 code. Anyways, check it out and let me know what you think. Useful? Garbage? Thanks!
Even still, if you ask it for token count it would still fail. My point is that it can’t count, the circuitry required to do so seems absent in these models
All the LLMs and multi-modal models I've seen lack concrete reasoning. For instance, ask ChatGPT to perform 2 tasks, to summarize a chunk of text and to count how many words are in this chunk. ChatGPT will do a very good job summarizing the text and an awful job at counting the words. ChatGPT and all the transformer based models I've seen fail at similar concrete/mathematical reasoning tasks. This is the core problem of creating AGI and it generally seems like no one has made any progress towards synthesizing something with both a high and low level of intelligence.
My (unproven and probably incorrect) theory is that under the hood these networks lack information processing loops which make recursive tasks, like solving a math problem, very difficult.