3 ms·
Zig looks overall interesting to me, but I don't like that it reproduces one of the pains of C++ templates, i.e. their dynamic typing. What I mean is, C++ templ
by yakubin 5y ago
Zig looks overall interesting to me, but I don't like that it reproduces one of the pains of C++ templates, i.e. their dynamic typing. What I mean is, C++ templates aren't checked before being executed, and instead you get compilation errors (at c++ templates runtime) which mangle the use and definition of templates, e.g.
template<typename T>
concept HasId =
requires (T a) {
T::kId; // "the expression T::kId is a valid expression that will compile"
};
// A and B are expected to be structs, where each has an associated integer constant kId and a field "int value".
template <HasId A, HasId B>
int min_id(const A a, const B b) {
if (A::kId < B::kIs) {
return a.value;
} else {
return b.value;
}
}
struct A0 {
static constexpr int kId = 1;
int value;
};
struct A1 {
static constexpr int kId = 2;
int value;
};
// etc.
struct A180 {
static constexpr int kId = 2;
int value;
};
int main() {
(void)min_id(A0(), A1());
(void)min_id(A1(), A2());
// etc. ending with:
(void)min_id(A179(), A180());
}
And now you get 180 errors about your typo (if not for max error number limits in the compiler), when really there is only one error in the definition of min_id. I used concepts to show that in the long tradition of C++, they're an additional feature that doesn't really help (quantity over quality of features).
Haskell solves that with typeclasses, Rust solves that with traits. What's really important about these solutions is that when you write a function generic over a trait or a typeclass, you are limited to using what is defined in the trait/typeclass. The trait/typeclass function both as documentation and as something that gets you better compilation errors.
"typename" in C++ is basically "any", but for values of template execution. Typeclasses/traits on the other hand are a sort of types for those values (which we normally call "types"). And similarly, in Zig there is an "any" type for types, just like in C++ --- it's called "type", and AFAIK there is no way to constrain your types with types of types.
- chakkepolja 5y agoIs the problem about C++ templates it's error messages or not being able to strictly type the arguments in type signature? I have a question to the compiler / IDE people here. In case of C++ templates, is it possible to output one error like: eg "class UserData needs to implement operator< to be used in min() function" instead of 200 lines errors? I mean, complex cases it's not possible but at least for simple ones like using an iterator variable directly instead of dereferencing it?
- yakubin 5y agoThose two are connected. Without strict typing the compiler doesn't know really whether the error is in the definition of min() or in its use, so the compiler mangles the two together and lets the programmer figure it out. > In case of C++ templates, is it possible to output one error like: eg "class UserData needs to implement operator< to be used in min() function" instead of 200 lines errors? This is already handled by concepts, but concepts solve only half the problem. E.g. in my snippet, in the comment you replied to, if any of the structs didn't have the kId constant, the errors would be about that. Concepts correctly detect when someone passes to them things that don't implement required functionality (as long as this functionality is specified in the concept). The other half of the problem, which they do not solve, is checking the correctness of the definition of the template against the concept. E.g. in my snippet I could use the "value" field, even though the concept that I used didn't declare it anywhere. At that point, if I passed a struct, which doesn't have that field, the compiler wouldn't be able to tell where the error is: in the template, or in its use --- so it would emit one error for each template instantiation, just as it did for "kIs" (because it can't know that it's just one typo in one place).
- tialaramex 5y ago> Concepts correctly detect when someone passes to them things that don't implement required functionality Unfortunately C++ Concepts check syntax even though as you'd expect the intention of the feature is to reflect semantics there is deliberately no way to capture that. So e.g. Rust has traits PartialEq and Eq reflecting things that claim to be partial or full equivalence relations. There's no syntactic difference, a Rust type can't syntactically be impossible to check for equivalence with some values and not others but e.g. Rust's f32 implements PartialEq not Eq due to NaNs whereas u16 has both since integers all exhibit equivalence. But in C++ there's just std::equality_comparable and it matches both integers and floats even though NaNs aren't in fact comparable because the float type syntactically has the == operator. So this means in C++ concepts matching means your program compiles but it might blow up at runtime thanks to a difference in functionality rather than syntax. To C++ programmers this is positioned as yet further responsibility on their shoulders. They must check that the data they're using actually models the concept that was required, it's not anybody else's job and if they get it wrong the program blows up.