> Hare gets around this by simply requiring all variables to be initialized as soon as they are declared. Since Hare is an expression-based language, variables which require logic to initialize correctly can be initialized from the result of any arbitrary expression, such as an if or match statement.

>

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