5 ms·
I am also of the opinion that `and` is more readable than `&&` (and isn't as easy to typo in a catastrophic way) - although my main point was about the weaker t
by fouric 5y ago
I am also of the opinion that `and` is more readable than `&&` (and isn't as easy to typo in a catastrophic way) - although my main point was about the weaker type system.
- qalmakka 5y agoC++ definitely hasn't a weaker type system than "newer" languages like Java - if any, it is much more richer and complex than most languages out there. What's happening here is a type conversion that has to be in place due to C not having a boolean type until 1999. C++ attempts to construct a boolean from the argument of an `if()`, and given that bool can be constructed from int, the conversion succeedes. You can define your own conversion operators to boolean, too, which are very useful for stuff like smart pointers and similar classes that may or may not have a value. struct A { std::string value; explicit operator bool() const { return this->value.size(); } }; // ... A a {}; if (!a) { // ... }
- fouric 5y ago"Not as weak as Java" is not a very interesting benchmark, as Java also has a poor type system. C++'s type system is pitiable relative to those of Rust, Haskell, OCaml, and SML. Moreover, in addition to being less expressive than them, C++'s type system is also weak, in formal sense, by allowing many implicit type conversions - which is one of the issues that I was complaining about. The fact that it "has to be in place due to C not having a boolean type until 1999" doesn't make it any less weak.
- jcelerier 5y agoWhich of "Rust, Haskell (core Haskell), OCaml, and SML" is able to parametrize types on values, à la template<auto> ?
- Macha 5y agoRust 1.51+: https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html https://blog.rust-lang.org/2021/03/25/Rust-1.51.0.html
- comex 5y agoIn extremely limited form. That should change in the future, though.
- jcelerier 5y agoIt only supports integers so far, which c++ could do pretty much since before it was even standardized 25 years ago. Doesn't seem to support as wide of a type menagerie as C++20's NTTP. Does it even support parametrization over function pointers ?
- tialaramex 5y ago> It only supports integers so far Integer-like things. C++ char is similar to Rust's u8 or i8 but Rust's char and bool are very deliberately not just integers. > Does it even support parametrization over function pointers ? Can you give a clear example ? I think the answer is "No" but I struggled to put together an application for the feature I'm imagining. You would like to define a type, which is parameterised not by the type of function, but by specific functions? So, for example I can make a foo<sum> and a foo<average> and those are distinct types which presumably internally are using the provided function to behave differently? Except all the examples I think of come out better with just parametrisation over traits instead. So a clear example from you might illuminate whether this is a sizeable hole or just a difference of philosophy.
- jcelerier 5y ago> Integer-like things. C++ char is similar to Rust's u8 or i8 but Rust's char and bool are very deliberately not just integers. sure, but there's an easy mapping from char / bool to integers. C++ supports parametrizing on values of struct type, which is a clear increase in expressive power: struct foo { int count; float init; }; template<auto arg> struct bar { float x[arg.count]; bar() { for(int i = 0; i < arg.count; i++) x[i] = arg.init; } }; constexpr foo f{ .count = 123, .init = 4.56 }; bar<f> b; > You would like to define a type, which is parameterised not by the type of function, but by specific functions? So, for example I can make a foo<sum> and a foo<average> and those are distinct types which presumably internally are using the provided function to behave differently? yes, it's pretty much the only way in C++ to have zero-cost callbacks / strategy pattern using function pointers since the compiler can directly inline the function pointer everywhere in the type's implementation instead of having to go and read it from some variable at run-time
- Akronymus 5y agoFor those languages, I don't really see a need to paremtrize types on values. Because of the AMAZING generics support. But that may just be the blub paradox [1] in action [1] https://wiki.c2.com/?BlubParadox https://wiki.c2.com/?BlubParadox
- qalmakka 5y agoWell the fact you couldn't reasonably use arrays without Generic Const hit stable in Rust 1.51 says otherwise. Defining templates on values is very useful, especially in C++ where you can provide template specializations. You can do a whole lot of metaprogramming and compile-time stuff that way.
- gpderetta 5y agoMuch more usefully, in C++ you can also parametrize over templates, which I think you still can't in rust. You can't parametrize over namespaces (at least not directly) which is an annoying and arbitrary restriction.
- account42 5y ago> You can't parametrize over namespaces (at least not directly) which is an annoying and arbitrary restriction. It's not a restriction, namespaces are neither types nor values so they would need specific support. Given that you can (ab)use classes with static members as namespaces which are also a type it's simply that nobody cared enough to add support for templating over real namespaces.
- gpderetta 5y agoYou are right it is not a restriction in the sense that is explicitly disallowed; what I meant is that it would take very little to add support for parametrizing over namespaces. Stateless structures are a workaround (so is adl driven by a template parameter), still direct support would be nice.
- qalmakka 5y agoThe fact implicit conversions exist doesn't really weaken the type system, the two are different concepts. If you define an overload or a type specialization on both the compiler will call the right version without any ambiguity. You can even define your own implicit conversions itself, and sometimes they have their uses, like when you need to wrap values in proxies but you still don't want the user to actually have to know that. The template system and stuff like auto and such are crazy powerful. The Rust typesystem doesn't suffer from the C compatibility baggage and has been designed almost 30 years later, so it is amazing C++ can be so powerful and feel modern despite it being a forty years old language. I write both Rust and C++ and I have to say, you can do a lot of type safe stuff in C++ too. Most patterns can be backported from Rust and while the ergonomics are not obviously at par, there is still a lot you can do. In C++you can do a crazy amount of metaintrospection at compile time that Rust can only do using procedural macros. Also constexpr and C++20's constinit and consteval are still more powerful than Rust's const.
- pjmlp 5y agoIt is nothing to be amazed, other than we are finally able to enjoy such type systems in mainstream languages. PL/I and Algol 68 were equally powerfull, or if you research into was being done in Xerox PARC workstations. If anything, we are 30 years too late for where we could have been if it wasn't for UNIX winning the worstation market.
- GoblinSlayer 5y agoJava has a poor type system, but not as poor as that of C++.
- qalmakka 5y agoThis is uttely false. I desume you haven't used C++ in the last 10 years, or at least you didn't delve deep enough into it to really understand how powerful (while bonkers) the C++ type system is. C++ has a much, MUCH more stronger type system with true generics, value types, const-correctness, compile time reflection and dispatching, ... In Java, everything is a reference, except when it's not (which is a design mistake that .NET fixed, IMHO). Some stuff in Java is plain "magic", like type erasure and boxing, while C++ might well be drowning in its own sea of utter madness but at least tries to be somewhat consistent (for instance, there are no "magic" types, when you do `int { 3U }` you are "constructing" an integer, when you do `bool x { 33 }` you applying the implicitly defined `bool(int)` constructor from bool. You can define your own conversions, and you can define your own custom types that behave and can be used like built-in ones (see smart pointers, iterators, ...). Java _seems_ stronger typed because it generally doesn't allow integer promotions and implicit conversions, but these are concepts that are orthogonal to the type system, `bool` and `char` are different, distinct types and if you specialize a template for T<bool>, it won't apply to `char` unless a conversion happens, and if it does so, it is still operating on `bool`, not char - it is constructing a type from another, the fact this happens is simply hidden from you, like Java and boxing (which ironically is an implicit conversion). The Java delegates pretty much everything to the JVM, and that's reflected in the language design. Java is a simple language that does not do a lot at compile time, relying on runtime facilities to mitigate these shortcomings. See for instance how everything can always decay to a reference to Object, implicitly, everywhere, requiring casts (i.e. runtime assertions) to restore type safety - that's basically a safer `void*`. 1995's Java was clearly too limited, I understand they wanted a fresh start from the ugliness of '90s C++, but they straight removed too much for the sake of simplicity. The current crop of languages, which largely rejected the Java model is kind of a symbol of what went wrong with Java, IMHO. The fact certain features had been "hacked" on top of the language using what was already there (see generics, boxing, ...), often introducing features that act like "magic", and can't be overridden by the user is bad, and shows how limited the original language was. The same way you can't override operators, you can't define custom boxing rules for your types (mostly because you won't be able to define custom value types until Valhalla is released). Modern C++ allows you to write safe and solid code using compile-time features and the type system. While stuff like <type_traits>, SFINAE, template metaprogramming and such are definitely not "nice", they are extremely powerful and if used correctly eliminate completely certain issues from ever happening. If you only use smart pointers, references, containers, moves and by-value semantics you won't get crashes from nulls, ever. You won't have memory leaks, and after lots of fighting with the compiler, there's a high chance your code will work straight away (unless you messed up the logic). This is not that far from Rust, as far as my experience goes - I still hope for Rust to mostly replace C++ in the end, but for now C++20 is a very solid substitute and a good choice (and feels much more modern and powerful than Java could or has ever been).
- adrianN 5y agoThe type system in C++ is pretty strong actually, it just has too many implicit conversions. It's not like the compiler doesn't know that this value is not a bool.
- gpderetta 5y agoI don't think weak has a formal sense in any formal sense.