5 ms·
The standard library being designed to allow freestanding binaries makes Zig a much better language for systems software such as kernels, EFI applications, etc.
by softirq 3y ago
The standard library being designed to allow freestanding binaries makes Zig a much better language for systems software such as kernels, EFI applications, etc. than Rust.
Rust's addition of operator overloading is also a perplexingly bad decision. I don't think there is a single worse design decision in C++ or Rust than allowing someone to redefine what + means on some arbitrary type.
- tnecniv 3y agoEh, I think operator overloads make a lot of sense in the right context. If you created a type that’s a kind of mathematical object, you want a mathematical syntax for it. However, they are horrible when abused.
- thatxliner 3y ago> Rust's addition of operator overloading is also a perplexingly bad decision. I don't think there is a single worse design decision in C++ or Rust than allowing someone to redefine what + means on some arbitrary type. Why is that? Won’t it be more ergonomic than needing to call, say `.add`, when you want to do something clearly similar to addition. It’s just sugar, no? In Java, there is no operator overloading, so if you want to compare 2 strings by contents rather than their memory addresses, you have to use a `.equals` method. Comparing 2 strings by their contents is the most common use case, so it not being the default is a design flaw (similar to how you need to `break` out of a `case` in most languages). If you can’t overload the addition operator, then why have a whole language construct that can only be used on primitive integers and floats?
- zozbot234 3y agoThing is, Rust has full support for hygienic macros so operator overloading could've been added as part of that. You'd just have to write, e.g. int_expr![a + b] or whatever, but that would've made the syntax fully extensible.
- softirq 3y agoMacros are easy to spot, the whole point of operator overloading is that it's a trojan horse. It might do simple addition, it might do a heap allocation and talk to a printer.
- softirq 3y agoWhen writing systems software like a kernel, clarity is far more important than ergonomics. The basic operators aren't functions, they're translated directly into opcodes based on the types of the operands. Operator overloading allows them to be either opcodes or functions, and if you think about the amount of work that goes into a function - saving registers, creating a stack frame, pushing addresses, multiple branches - versus a single opcode, as a kernel developer you really don't want this to be hidden behavior based on whether something has been overloaded or not. In the best case the compiler will optimistically inline multiple instructions, in the worse case it will call a function before you've even set up a stack.
- zozbot234 3y agoSIMD intrinsics are also translated directly into opcodes. There's very little reason to have floating point ops represented by special syntax rather than ordinary functions like fadd() fsub() fmul() fdiv() fmuladd(), other than mere legacy. (And they might not even be simple opcodes in a soft-float implementation.) I mention floating point specifically because that's where even something basic like the order of operations can affect the outcome, so the extra precision actually matters.
- gpderetta 3y agoWhat if your target cpu doesn't have, say, floating point operations, or integer division. Will Zig refuse to compile any code that uses these operations? Will it inline the emulation code? Or gasp generate a call out to a library function?
- softirq 3y agoThis is an extremely common issue to deal with when creating a kernel, which is, again, why levels of indirection in code and hiding context only makes life more difficult for a systems developer. The kernel may need to enable accelerators, switch between arm and thumb, enable an fpu, clear cache lines, etc. A lot of decisions will be made by the compiler, but based upon parameters passed in at build time, and a lot of it will be arch specific code interwoven in assembly. And there are tons of times when a compiler will generate things you don't want, forcing you to add pragmas and so forth.
- mondobe 3y ago> Why is that? Won’t it be more ergonomic than needing to call, say `.add`, when you want to do something clearly similar to addition. It’s just sugar, no? Especially since, in a typed language, this function would be desugared at compile time and (probably) aggressively inlined, making it hardly different from the compiler builtins for adding floats and ints. Unless, of course, you're doing something like allocating to concatenate strings. Really, though, it's more of a developer common-sense issue than a language one.
- pie_flavor 3y agoNative ints support math operators because math operators are far more readable than method calls. And if you're doing something that requires a custom numeric type, like base-10 floats or fixed-width, method calls don't get any more readable than math operators there either. A language where you can't overload numeric ops is a language with a strong disincentive against using better numerics. And this is without getting into having a single source of semantic equality, whether it be `==` or otherwise; not having that is probably the single worst design decision of Go. This complaint is always made in the context of some pathological case where a library author tries to do clever things with operator overloading; if any such libraries exist in the first place, you can guarantee that you are not using them by simply not using any libraries with fewer than 10k downloads. The horror stories that fill HN comments pretty much never make it into real code.
- softirq 3y agoSee my other comment about the necessity of clarity in a systems language. Also in my opinion readable code is code that is easily understood with as little context as possible. Operator overloading is the opposite, it hides behavior based on the types of the operands. Instead of thinking, that * will turn in to an smul, we now have to check for and read the implementation of * for every type. A function makes it clear that there is a call site and the types of its operands, and the name can more clearly convey the meaning of what is to be done than *. What you are referring to as readability is actually terseness, which I think is a lousy metric to optimize for, especially for systems software where correctness is important and people will read code a lot more than they will write it.
- WalterBright 3y ago> I don't think there is a single worse design decision in C++ or Rust than allowing someone to redefine what + means on some arbitrary type. This has perplexed us for D. Experience with C++'s iostream's operator overloading meant running away screaming. Another terrible thing is people would code up DSL's using operator overloading, such as a Regex language. The horror there is the source code looks like ordinary C++ arithmetic, but it is actually doing Regexes. So, how to allow operator overloading for arithmetic, but not for other porpoises? 1. Only allow overloading of arithmetic operators (i.e. no overloading of unary *) and [ ] 2. Only allow < overloadable, instead of < <= > >=. This enforces symmetry. 3. Don't allow overloading of && || ?: 4. A strong Compile Time Function Execution feature which enables DSLs in the form of string literals 5. Develop a culture of operator overloading is for arithmetic This has worked well.
- zozbot234 3y ago> Another terrible thing is people would code up DSL's using operator overloading, such as a Regex language. There's nothing wrong with this if your language has proper hygienic macros. Then you can have all of math_expr![ … ], float_expr![ … ] (dangerous! order of operations may affect results), regex_expr![ … ], or even stream_concat_expr![ … ] all using the same operators while meaning completely different things and preserving complete extensibility. They would even be composable since each macro invocation would desugar its own operators and leave those in other contained macros unaltered.
- WalterBright 3y agoMacros form their own hell, hygienic or not. The reason is inevitably these evolve into one's personal, undocumented, quirky, unmaintainable language.
- avgcorrection 3y agoThis sounds great if you’re stuck inside the C-derivative mindset block. I.e. the best you can do.
- brabel 3y ago> Rust's addition of operator overloading is also a perplexingly bad decision. You know, I don't even have a side on this never ending debate... but it's perplexing that similarly intelligent people, with similar interests and backgrounds can both make so confident blank statements such as this, one way or another! IMO that is a pretty good indication that there's no right answer, it's simply a matter of preference... and the fact that people still feel like they're right and the people who disagree with them must be making "perplexing bad decisions" is, for lack of a better word, hilarious.
- avgcorrection 3y ago> Rust's addition of operator overloading is also a perplexingly bad decision. Yes. The poor man’s custom operators.