Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Odin, Zig, Jai, Hare, etc - all of these new languages have been mostly inspired by Go, C, and Rust.

So let's summarize:

1. Simplicity and readability - C, Go

2. Tiny language - C, Go

3. Modularity - Go, Rust

4. Defer statement - Go

5. Metaprogramming (generics, compile time, macros) - lots of inspiration and some really fresh ideas like Zig and Jai, Go interfaces, and Rust traits look nice

6. Strong type system - Go, Rust

7. Manual memory management, pointers - C

8. No OPP in terms of C++, Java

9. No references, just pointers

10. Syntax - Go, Rust

11. Zero cost abstraction and as much as possible minimal runtime - C, Rust

Mostly their look like Rust with "defer" but without borrow checker, move semantics, references, RAII, and lifetime annotation.

Mb this is what we really need? :)



Putting Rust and Go together under strong type system is a very odd choice.

Rust is inspired by ML family languages and heavily leans on it's type system, while Go is very simplistic in comparison. (This has improved somewhat with the recent addition of generics)


Yup. For example, in both Rust and Go you can cheerfully open a filename, based on a string you found in some JSON. But the reason why you can do that is very different, and reveals important foundational differences.

In Go the answer is that strings are just some bytes, and filenames are just some bytes and so this naturally just works.

In Rust the answer is that strings are AsRef<Path> and you can open a Path, so when you call open the compiler gives it a Path even though that isn't what you actually had (you can't mutate the Path via this reference, so we know open doesn't change it).

The difference becomes more stark if we go the other way, starting from a list of files in the current directory and writing a JSON file.

In Go if you get a list of all the filenames in the current directory, it's a list of strings, and the fact that those aren't actually text is your problem, you will need to explicitly take care of this or you can't emit valid JSON.

In Rust, you get Paths, and you're going to need to explicitly ask for the strings to make JSON, at which point you have to decide what you want to do if the Path isn't just text, you're obliged to decide, even if it's just panic (ie abort the program).


> In Go if you get a list of all the filenames in the current directory, it's a list of strings, and the fact that those aren't actually text is your problem, you will need to explicitly take care of this or you can't emit valid JSON.

You'll still emit valid json. The encoding/json doc says:

> String values encode as JSON strings coerced to valid UTF-8, replacing invalid bytes with the Unicode replacement rune.


The original filenames will be lost though, if they weren't utf-8.


But to be fair if we're sending them as UTF-8 in a JSON file we can't do anything about that.

I've had situations where I am obliged to write output to XML, and clients are like, "You can't lose this weird data in our text". That's not me, that's XML, in XML 1.0 most of the ASCII Control Characters are banned (not like "You must escape this character" they are banned if you write them as text, escaped or not, that's not a valid XML file and some parsers won't read it). You can either admit you want binary data maybe wrapped as Base64 inside the XML or you can accept that in XML those characters are toast. I would turn all the banned characters into U+FFFD in XML.

JSON doesn't have that defect, but it still can't magically make non-text into text, and some filenames are not text. So this feels fair enough. Chances are if your JSON format is so capable that you can write "Here's a Non-text filename" and have that work almost all the people parsing it ignore that case and you didn't really improve interoperability at all.


Aha, useful. Good catch.


I agree - Go's type system would be perhaps better described as strict. There are rarely cases in which conversion is implicit (not particularly unique to Go, but useful). Between types are aliases of those types? No. Between signed and unsigned? Of course not. Between string and []uint8? No. What about less precise to more precise? Nope. This can be a pain, but overall it avoids some classes of bugs present in languages like C (without getting strict about your compiler flags anyway) and allows you to use types to encode your problem in a way that prevents dumb mistakes.


Especially with how Go handles default values. Suddenly a value wasn't present in a deserialization and now that's a nil pointer. Or if you have a stateful 0 so you can't tell the difference between missing or user choice without a deeply awful extra check.


A couple scattered thoughts:

- Defer is nice for cleanup, but I think destructors are the gold standard there. They're even simpler to reason about, you can't forget to invoke them, and combined with move semantics they automate away a really wide range of cleanup scenarios. Another interesting difference is that adding destructor-based cleanup to an existing type that didn't previously have a destructor is often a backwards-compatible change. Lastly, GC'd languages like Go usually need to include some sort of finalizer mechanism in addition to the defer syntax, and finalizers are surprisingly complicated.

- I don't think C should get full points for zero cost abstractions. There's a lot of pressure to use void*'s or intrusive structures in place of true generic containers like std::vector/Vec, and that comes with runtime overhead in practice.


If you are going to mention Odin, Jai, and Go then you should mention Vlang too (https://vlang.io/) because it's in that category.


You could say it is a Cambrian explosion of non-JS langs, if you will.


Zig should go under small language and modular, imo


12. Explicitness!!!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: