5 ms·
> Just remember, if you're ever debugging something and some values in a Class/Object/Group of them are set and others are unset the state machine avoids that i
by munchler 2y ago
> Just remember, if you're ever debugging something and some values in a Class/Object/Group of them are set and others are unset the state machine avoids that issue.
Better yet, switch to a language that supports discriminated unions to avoid invalid state without having to write a low-level state machine. Each case in the union represents one of the valid states of the type. This is one of the many benefits of functional programming.
- tialaramex 2y agoThis comes down to "Make Invalid States Unrepresentable" Notably Go's choice here is instead "Make Representable States Valid". So for example in Go it's not possible for a type not to have a default value - every type must have a value you get by default, you could name this unwanted default value FUCK_OFF or DO_NOT_USE if you like, but in a larger system (where Go is supposed to thrive) you can be certain you'll find FUCK_OFF and DO_NOT_USE values in the wild. This is one of the few things the C++ type system almost gets right. You really can say for your C++ type no, it doesn't have a default. Unfortunately exception safety means we can easily end up in a place where too bad it's in some state anyway, but if we turn off exceptions or discount them we can live without nonsensical defaults.
- typon 2y agoMaybe something like CppFront can allow metaprogramming such that the default constructor is deleted by default on all types and becomes opt-in.
- ekidd 2y ago> This is one of the few things the C++ type system almost gets right. You really can say for your C++ type no, it doesn't have a default. Rust does even better here, I think, for fairly subtle reasons: - Exceptions are replaced by Result, with explicitly-marked return points. They're far less magic. ("panic!" is still magic, but it's allowed to be a call to "abort", so you only use it when the world is burning.) - "Move semantics" by default makes a lot of edge cases cleaner. If you move out of an object by default, it gets consumed, not set to a default value. Similarly, moving values into a newly-constructed object can't fail, because it's just memmove. - Object construction is atomic from the programmer's perspective. - Default initialization only works if you implement "Default". Which is actually just a normal trait with no magic. - If you discover that you have a state machine, you can switch to discriminated unions ("enum") to make your states mutually exclusive and clearly represented. This whole area is one of the better parts of Rust's design.
- kelnos 2y ago> Default initialization only works if you implement "Default". And -- and I think this is important -- even then, you have to explicitly assign the default value[0]. You won't just somehow magically get a default value. [0] E.g.: let foo: SomeType = Default::default();
- deleted 2y ago[deleted]