Yugo vs. C++
-
Yugo does not implement inheritance on classes/structs. Dealing with the quirks that come with inheritance especially multi-parent was a bit of a pain to get correctly. The Diamond Problem in C++ is a good example of the challenges arising from supporting multiple inheritance. The last time I used inheritance in C++ was for AST nodes in Aburi, but I am not a big fan of OOP-based AST nodes: the Aburi “Derleyici” release notes give a rough idea why.
-
Yugo does not have over the top template/generic machinery. Partial specialization, template type deduction and decay rules, two phase lookup, SNIFAE, and more are a significant source of complexity in C++/Aburi.
As a particularly contrived example, the following code compiles:
#include <concepts> template<class T> concept Measured = requires(const T& value) { { value.measure() } -> std::same_as<int>; }; template<Measured T> int broken(const T& value) { return value.measure().missing(); // int has no methods! } struct Reading { int measure() const { return 67; } }; int main() { // No call: Clang (and Aburi! https://godbolt.org/z/G3raGsqPa) accepts this program. // return broken(Reading{}); // Uncomment: compilation fails. }The template will never be valid yet it compiles just fine. Because
.missing()is basically syntatic sugar until the template is instantated, the semantic analysis layer will need more information from the parser (whether in the form of a detailed AST or token replay), complicating the instatation procress. Compare this to Yugo, where the below code is a compile error:protocol Measured { func measure() -> Int } func broken<T: Measured>(_ value: borrowing T) -> Int { return value.measure().missing() } -
No namespaces.
-
No exceptions.
-
Yugo is not a C++ successor language like Carbon or Quxlang, and does not aim to be.
Yugo vs Swift
-
Some of the syntax is different.
var gen_z_brainrot: [Int; 2] = [6, 7];isvar gen_z_brainrot: [2 of Int] = [6, 7];in Swift?operation on optionals propogates instead of silently failing (like Rust)
-
Unsafe operations are “first-class”:
*and&is a shorthand for the UnsafePointers.- Writing “unsafe” (I know the word “unsafe” a bit of a misnomer as “memory unsafe” languages are merely ones the compiler does not guarantee the safety of) code is a supported use case and I aim to make it easy for programmers going down this route but it is not a “unsafe first” language like C, Zig, or C3 is. The ergonomics of “unsafe/raw” code in those languages might be better compared to Yugo.
-
No ARC classes. To get similar behavior in Yugo use the
Shared<T>type on a struct. -
Extensions are not supported. We recommend using a technique similar to Newtype in Rust.
-
Borrow checking is first class, althrough it can be disabled. Borrowing infrastructure was added in Swift 6.x. I forget the exact version.
-
Computed Properties are not supported. The implict behavior here is worse than operator overloading IMHO because with overloading one can glance from the variable definition if it’s nearby and explicitly type, that overload function will be called. With computed properties the only way to know is by looking at the structure directly.
-
Overloaded operators not supported (yet?) I’m still debating internally if to add or not.I added overloaded operators but they are kept on a tight leash. Overloading is only allowed with other variables of the same type, or a scalar of which type is determined at struct declaration time. This prevents overloading and type promotion/demotion concerns. -
No macros. Any metaprogramming needs should be handled by the static reflection/comptime facility offered or by having your agents write deduplicated code (think the approach of Golang’s
go generate).- The metaprogramming capabilities are intentionally kept small enough to be useful while not being large enough it becomes the focal point of entire language.
General differences/similarities (Rust, Go, Zig, …)
- Yugo uses a decentralized package management system with URLs/hashes similar to Zig. Aside from the obvious problems with centralized package systems, I don’t the wherewithal to create infrastructure for a programming language no one is using (yet?)
- LLMs make it easy to generate code for your use cases. Dependencies should be kept to a mimimum as a general language guideline. Supply chain attacks are a real concern and cyberattack-capable LLM models only worsen the situation.
- If there is a package/crate/library so important its imported everywhere or needed for a language feature to be usable (e.g: Rust async and Tokio) then it should be part of the language standard library.
- Unlike in Rust, the borrowing mechanism here can be disabled. I envision a potential workflow involving intensive cycles of editing, compiling, and debugging, during which the checker is disabled most of the time and activated only at specific moments. I do not want a situation to arise where it becomes common practice to compile libraries and projects with borrow checking disabled; that capability should be reserved strictly for internal development processes.
- The borrow checker is (intetionally) not as advanced as Rust’s and it may reject code that Rust might accept under similar conditions. In these case you can use the “unsafe” facility: it is not frowned upon in Yugo whereas it might be in Rust (obviously try to write “safe” code as much as possible,
Sharedexists )
- The borrow checker is (intetionally) not as advanced as Rust’s and it may reject code that Rust might accept under similar conditions. In these case you can use the “unsafe” facility: it is not frowned upon in Yugo whereas it might be in Rust (obviously try to write “safe” code as much as possible,
- Indexing a hash mapping using
[]operators is supported only when the key exists in the map. It’s a bit counterintutive compared to languages like C++ and Python which create the key:value on demand if needed.