Frequently asked questions

Why would I want to use Hare?

Hare is designed to be a small, simple, and stable programming language that punches above its weight on systems programming tasks. Hare is a language that most of its experienced users should be able to understand in its entirety: it’s feasible that one person can read and understand the entire compiler, for example. This also becomes easier given that Hare is self-contained – it does not depend on libc. Hare is also designed to be stable for the long-term: when we’re done with Hare 1.0, the language syntax and semantics will freeze and it can be depended upon indefinitely.

If the trade-offs make sense for your use-case, Hare may be the language you need; otherwise, it’s not. Hare is not a “kitchen sink” language: Hare does not attempt to solve every problem, but it does strive to solve the problems we’re interested in well.

How does Hare compare to other languages?

Compared to other languages in Hare’s niche…

C: Hare should feel comfortable for C users, but with most of the warts fixed. It has namespaces, much better error handling, first-class UTF-8 support, many quality-of-life features, and a much better standard library. Almost every problem C can be applied to, Hare can also handle, all the way down to kernels and bootloaders. Hare uses a superset of the C ABI and you can directly call C functions from Hare and vice-versa. C programmers should find Hare a simple, intuitive upgrade from using C.

Go: Hare shares some features with Go, such as defer and a similar standard library design. However, the language design differs in many respects: Hare does not use a garbage collector, lacks support for generics, and has a more comprehensive type system and error handling support compared to Go. Hare also lacks Go’s concurrency features and userspace scheduler, which, depending on your perspective, is either a drawback or an advantage. Many Go users who are frustrated with the difficulty of performing low-level systems programming tasks in Go find Hare well-suited to their needs.

Rust: Hare is much, much simpler than Rust. Hare lacks generics, traits, macros, and many more features commonly used in Rust. Hare also provides fewer memory safety guarantees than Rust. Moreover, Hare is culturally distinct from Rust, for example we have no package manager and encourage less code reuse as a shared value. However, Hare has a comprehensive language specification, a much faster toolchain and build times, and Hare programmers tend to identify simpler and more maintainable solutions to the same problems. If you’re happily using Rust, Hare is probably not for you; if you are unhappily using Rust, consider trying Hare.

Zig: Hare is simpler than Zig, both in design and implementation, though they share some features and design, such as defer and built-in tests. Hare lacks comptime, generics, or other forms of metaprogramming. Hare’s approach to error handling and tagged unions are more generalized and flexible than Zig’s approach, allowing you to add more context to errors as necessary. Hare’s standard library more comprehensively supports features a C user may expect, such as regex, comparatively improved string operations, date/time arithmetic, and so on; whereas Zig’s standard library has more support for higher-level or novel features like HTTP and TLS.

Why qbe instead of LLVM?

Hare’s backend, unlike many new languages making use of LLVM, is based on qbe. qbe clocks in at a mere ~15,000 lines of portable C99 code; LLVM is counted in the tens of millions. The ability for a single programmer to fully understand the Hare toolchain is one of our goals, and this rules out LLVM.

This does not come without trade-offs: qbe generates slower code compared to LLVM, with the performance ranging from 25% to 75% the runtime performance of comparable LLVM-generated code, depending on the workload. Often this can be mitigated by hand-writing assembly in hot sections; for example Hare’s standard library utilizes Intel’s AES-NI x86-64 instructions when available, achieving substantially better performance than an AES implementation compiled with either qbe or LLVM.

Will Hare support Windows or macOS?

Hare does not and will not officially support proprietary operating systems upstream. However, Hare is a standardized language and third-parties may build and maintain forks or compatible implementations of Hare targeting these systems.

A macOS port is independently maintained here:

https://github.com/hshq/harelang

Warning

These ports of Hare are not supported upstream! Do not report bugs or ask questions related to these ports in the upstream Hare community spaces or development channels.

We understand that a well-designed system must be based on free software. We cannot effectively study, understand, debug, or improve, the underlying operating system if it is non-free. We actively work with the source code for the systems on which we depend, and we are not interested in supporting any platforms for which this is not possible.

The goal of Hare is not to achieve the broadest possible reach, but to be a part of a broader system which effectively achieves Hare’s goals. Proprietary operating systems are not compatible with this requirement.

Is a language server available?

Not yet, but maybe you’ll write one? Ask about it in the community spaces.

Can I use multithreading in Hare?

Probably not.

We prefer to encourage the use of event loops (see unix::poll or hare-ev) for multiplexing I/O operations, or multiprocessing with shared memory if you need to use CPU resources in parallel.

It is, strictly speaking, possible to create threads in a Hare program. You can link to libc and use pthreads, or you can use the clone(2) syscall directly. Operating systems implemented in Hare, such as Helios, often implement multi-threading.

However, the upstream standard library does not make reentrancy guarantees, so you are solely responsible for not shooting your foot off.

Why doesn’t Hare have generics?

Omitting generics (and similar features) in Hare is a deliberate design choice which simplifies the language considerably and is more aligned with its design roots in C.

Our semi-official casual explanation of this choice is that many programs have, at most, one or two really important data structures central to their design (and responsible for their bottlenecks), and because these data structures are important and central to the design, it’s wise for you to implement these yourself so that you can (1) understand them and (2) adapt them to your specific use-case. Optimizing every other data structure that your program makes use of, but which is not bottlenecking your performance, is a premature optimization.

So I need to implement hash tables myself?

Indeed. Hash tables are a common data structure that many Hare programs will need to implement from scratch. Thankfully, they are generally straightforward to implement for most use-cases. A simple example is provided here. The standard library provides suitable hashing algorithms to simplify this for you:

Why so many semicolons?

Hare is an expression-based language, and it simplifies Hare’s context-free grammar to use lots of semicolons. We might change this at some point before Hare 1.0.

That’s not a hare, it’s a rabbit!

The mascot is a baby hare, which looks like this:

Photograph of a baby hare, which looks similar to the Hare mascot

Photo from Alberta Institute for Wildlife Conservation