Safety features of the Hare programming language
harelang.org
harelang.org
Side note - Drew, your Gemini capsule was one of the few I regularly check! I thought my internet was down when it wouldn't connect. Hope to see you back on sometime.
I also think two of the principles you laid out - ease of implementation and transparency/explicitness of code are the hottest takes in Hare. I can see why people like or dislike those choices, and it's compelling to think about, but I'm not sure that the explicit choices to add or not add features from PL research reflect those.
For example the paragraph on GC - I think there's a ton of reasons not to use a garbage collector in a language like Hare (for example, you have to write a GC in some language and not the one that is GC'd) but I'm not sure the reasoning you laid out is among them (concurrent and hard-realtime GCs are thing, whether removing the burden of manual memory management is more/less transparent to the intent of a programmer is very debatable).
> for example, you have to write a GC in some language and not the one that is GC'd
This is not true.
Honestly, I kind of expect them to walk back on the borrow checker promise for that reason alone.
I guess people like to role play as venture capitalist asking the critical questions or something. It super annoys me because is reduces my own enjoyment as I love learning about new things.
Also, a few up-and-coming languages are trying to fit into the C-replacement niche, so maybe it's some misguided fanboy-ism coming up.
Personally, I am just glad Drew decided to share Hare with us. Always interesting to see what trade-off are made in a certain language and why. Definitely like the minimalism, will keep an eye on it.
On borrow checkers: There is a big dichitomy between type systems and static analyses, where type systems aim to be sound---never fails to catch certain bugs, at expense of false positives---while static analyses tend not. But Rust's borrow checker is a sound static analysis combined with a type system. In fact it is a kind of optimal design when you are only allowed to add additional kind of types, and this restriction made the borrow checker a very impressive achievement. But there are other possibilities if you loosen a bit---either slightly unsound analysis [1] or annotation methods other than types. I believe Hare should better explore those possibilities instead of having a borrow checker.
[1] In fact Rust has already compromised as well; for example its notion of memory safety doesn't ensure no memory leak. It is still unlikely but not impossible to unintentionally leak memory, so it is deemed acceptable. You can use a similar compromise in Hare.
It's not even as though their hand-rolled mechanism would be syntactically heavier, you can't thing.leak() because leak intentionally isn't a method, it's just an associated function on Box and so you will need to explicitly Box::leak(thing)
I don't think Hare's design is at all conducive to trying to retrofit a borrow checker, and so I would agree with you that it should not attempt such a thing.
> Garbage collection magically stops your code (sometimes at predictable points, but it’s generally not very explicit), which is not very transparent and is definitely off the table for use-cases like kernels, video games, real-time applications, and so on. The behavior of a Hare program should be easy to predict, and a garbage collector interferes with that goal. Thus, despite the fact that a garbage collector would improve safety and ease-of-use, it’s not a good fit for Hare.
From what I understand, reference counting doesn't stop the code. However, it might not meet the goal of "Transparency and explicitness in code". Maybe it's too much for kernels, video games, real-time applications too.
However, you can definitely implement reference counting in your own programs and libraries.
The borrow/mutability checker are fairly easy to use (as long you stay on simple Rust: Only enum, structs, functions and not get with traits + generics + lifetimes annotations, and today: async).
Playing with the simple subset of Rust the borrow checker is fairly easy to get. I don't think nobody get stuck at this stage, only when start gettin ambitious and is not clear how ALL the features must intermix.
If the language is truly simple, I think it will work wonders.
Kudos!
>
> “trust the programmer, but not by default”. Every one of the features I have explained now can be circumvented, by design, if the programmer tells the compiler that they know better.
I wonder what the circumvention is for uninitialized memory, which is important for large data structures in high performance code.
> The “!” operator shown here is an error assertion, which promises that the error cannot occur. The compiler will check your work at runtime, and terminate the program if the error does indeed occur — you can simulate this via hare run main.ha >/dev/full. If we removed this operator, we would get a compiler error, because you cannot ignore errors.
This is similar to Rust's `.unwrap()` but even more invisible. I already wish `.unwrap()` was `.unwrap_or_panic()` to both make it more cumbersome, discouraging its use, and to make it easier to search for compared to the other `unwrap_or`s that exist.
> This one might raise eyebrows, but I consider Hare’s lack of a package manager to be an important security feature. There have been thousands of packages compromised on countless language-specific package managers: npm, PyPI, RubyGems, Cargo, and more — none of them have escaped this. This comes down to a fundamental problem in their design: arbitrary people on the internet cannot be trusted to publish packages directly for user consumption.
This also has to be offset with the risks that come from people writing their own code or copy/pasting it and losing track of where it came from for when a CVE is released.
This also doesn't deal with the other challenges:
- You can't predict what your code is going to build against when relying on Linux distributions
- You are stuck with the lowest common denominator of the distributions you support (which implies you are having to track what distribution versions are compatible with your project)
- Care is needed to ensure you don't end up in dependency purgatory where two programs need incompatible versions of a dependency
- Bootstrapping a new dependency or developing features in tandem has a high cost
Overall, if the friction is too high, people will go with suboptimal solutions, whether just not creating as feature-rich projects or creating ad-hoc "dependency management solutions" that are even worse than the problem being avoided. This is reminding me of a recent /r/cpp / /r/rust thread where someone was amazed at how fast the standard recursive directory walker was in Rust compared to C++. The Rust one is an algorithm from ripgrep split into its own library for others to use it, so you get the benefit of all of the research that was done to make ripgrep fast. Without cargo, we'd have a lot more siloing of code where you couldn't take advantage of the code people wrote for their program in your own. It'd be hard to be aware the code existed in the first place and it'd be more entangled, harder to extract it for your own use.
Actually there's not really much of a means around this. You could use static memory, which is only initialized once, and you can mmap pages directly from the kernel without initializing them, but allocating uninitialized data on the stack is not possible afaik.
Hare looks really interesting to me, and I want to give it a try, but this is frustrating.
Whatever the author's intentions are, if there is no built-in package manager, 3rd party package managers will pop up as soon as Hare catches on. All the problems he mentions will arise in those, and any development effort to tackle those problems will be split across multiple competing package managers.
I will also point out that Hare's approach is similar to C, and no attempts to make a C package manager have caught on. It's also helped by the fact that Hare does not support proprietary operating systems IMO.
> Hare does not support proprietary operating systems
I imagine a blessed implementation would be good here as well.
Anyhow, best of luck, it really does look good. I've struggled to find "a better C" that doesn't come with downsides severe enough to tip the scales back towards C.
The BIGGEST problem with package management is not package management, is how easy is to publish it and the lack of verification for authors.
The proposed solution already use the practical solution: FRICTION and VALIDATION (by somebody else). (and lets add namespaces!)
A relatively easy way to solve this:
- Add namespaces by default, and LOCK the "main one". - When a solid library emerge (like for example Tokio in Rust), you give it a star or similar saying: This guys are ok.
Split the package manager into 2 worlds: The walled garden and the chaos
- The walled garden requiere verification with some decent amount of friction and/or reputation (like check into GitHub?)
- The chaos is whatever.
- Link to walled garden is ok. Link to chaos requiere a "unsafe" parameter or similar.
P.D: Look like this sound like a good project to have (ie: A solid package manager manager!)
If the dependency is dynamic library, it is already handled by OS distribution.
If the dependency is compiled-in, then one can just use git to load dependencies as submodules.
I had this thought too when reading Hare examples. When looking at a line like this:
fmt::println("Hello World!")!;
I notice that the punctuation at the end of the function call blends together, and the error assertion operator is easy to miss. I prefer Zig's convention of requiring the 'catch' keyword anytime a function call can raise an error.An example from Zig's documentation:
const number = parseU64("1234", 10) catch unreachable;
This error assertion stands out (especially with syntax highlighting).> This plays into Hare’s goals for explicitness and transparency, and improves the saftey and predictability of the language.
Hare’s package manager is curl|bash? How is that a security feature?
‘But I am going to tell people not to do that!’ As if people are going to bow down before the unstoppable appeal of your charisma instead of following the path of least resistance.
Of course, this is all assuming there will be meaningful external Hare packages in the first place.
Hare is a conservative language which strongly encourages a conservative approach. We are not here to move fast and break things. Culture comes from the top-down, and while we may not succeed for all users, we can certainly set the standard of quality for the language.
C has no successful independent package manager, yet we rarely if ever see people distributing C libraries with curl|bash.
Third-party repos would have been a better answer, IMO.