6 ms·
The very first guideline is Month month() const; // do int month(); // don't I know I am in the outlier here but I very much prefer the seco
by distcs 4y ago
The very first guideline is
Month month() const; // do
int month(); // don't
I know I am in the outlier here but I very much prefer the second way of writing code. Sure the first one gives more compile-time safety but as someone who has to read the code, I have to go through an additional level of indirection before I truly understand how Month is implemented.
Others might offer a counterpoint that hiding such implementation details is exactly the point of OOP. True but as a developer who has to read and fix code, I do have to worry about the implementation details because the bugs could be in implementation details and hiding simple integers behind an extra layer of types adds to one more step when I am trying to reach the implementation details and fix bugs there. Multiply this experience by the number of all the layers in the code and maybe others might be able to appreciate why I prefer the second type of coding. Please someone tell me I am not the only one who feels this way!
- sqeaky 4y agoIt doesn't need to be about OOP and hiding details, this seems like basic type safety. Both of these are member functions and are hiding details already, so OOP is an aside, you would need invest effort reading on both or neither if there are robust unit test that solid OOP would enable. With ints as months is 0 or 1 January? What about -1 and 13, do they have sensible meaning? You need to re-apply that effort constantly to any int that might be a month. This isn't a hidden implementation this is an easy mistake waiting to happen. Using a month enum solves this, and you are not fixing bugs in the implementation of an enum unless you are a compiler developer. Same with const, it prevents accidental changes to the class from this member function. It allows more sensible code and compiler optimizations. The "do" line will almost always be faster than the "don't" line. There is no way adding const changes the amount of effort for a reader in a negative way.
- Jtsummers 4y ago> this seems like basic type safety. Please stop sharing my secrets. I've done pretty well fixing up things with basic type safety because my predecessors couldn't be bothered to use their brains. I like being employed.
- account42 4y ago> It allows more sensible code and compiler optimizations. Unfortunately no, const only tells the compiler optimizer something when applied to variables but not for reference or pointer types (or member functions, where it just means apply const to the this pointer). It would be nice if there were a stronger co_const that would tell the compiler could assume is never cast away (or at least that the variable is not modified through that pointer/reference even if the const is cast away) but C++ const is not that.
- kllrnohj 4y agoThe const in this example is on the member function so nothing external can cast it away, but the value of it there is really more about contract/behavioral enforcement than optimizations yes.
- kllrnohj 4y agoNothing about that indicates it's OOP or not. > I have to go through an additional level of indirection before I truly understand how Month is implemented I'd just hover over it in my IDE and it'd tell me what it is. Regardless, "Month" tells me it's a "month" whereas "int" tells me "lol get fucked I ain't telling you shit" The "do" example there is much more readable than the "don't" example. Especially in any further usages of it, as it's one less thing I need to keep track of. That is, I don't need to second guess if the variable was passed to the wrong thing since the compiler is validating that for me. I don't need to track that during my "reading" > True but as a developer who has to read and fix code, I do have to worry about the implementation details because the bugs could be in implementation details Then why are you arguing against it? A "Month" type cleanly restricts the implementation details to a specific controllable set, which an "int" does not and is a free for all. You're in for a nightmare trying to fix people doing math with dates if you use "ints", whereas you can pretty easily fix all of the users of a "Month" or "Date" type by just removing the math operators (or "fixing" them to work appropriately) and letting the compiler spot all the mistakes.
- MaulingMonkey 4y ago> Regardless, "Month" tells me it's a "month" whereas "int" tells me "lol get fucked I ain't telling you shit" I disagree. Is "Month" a 1-based month ordinal? A 0-based month index? A string containing user input that should be preserved in it's original form to avoid data loss (be that "jan", "JAN", "January", "1", ...)? Memoized? An indirect reference to OCR data? Do I even have a guarantee it's on the Gregorian calendar, or am I in the context of some calendar app that might support chinese lunar calendars? Is it JSON serializable? Is it a localization placeholder? Is it an integer typedef that means different things in different contexts, including "months ago" in some relative timestamp formatting code? "Month" tells me jack shit that the function and variable names didn't. "int" at least tells me I can math it, and that if I'm dumb enough to have memcpy-serialized code, I have a potential porting hazard in the form of endian issues - and size variance if I'm dumb enough to target an ILP64 platform. Based on personal experience, these are far more likely bugs for me to encounter and need to fix than any that would be caught by stronger month typing. There is a place for stronger types - I'm not complaining about Rust's SystemTime/Instant/Duration trifecta and more - but "Month" ain't it. It's a bridge too far, too pointless - it is the wrong abstraction. And, yes, you can math on months. For indexing into arrays of localized strings, for conversion to/from epoch... I've written some really nasty .natvis visualizers that rendered absolute timestamps as PST timestamps for debug convenience, and it involved a whole lot of rather redundant and duplicated math in that horror of an XML format which would be made 10 times worse by extra "help" in the form of strong types to be unwrapped. > I'd just hover over it in my IDE and it'd tell me what it is. This works until some jerk buries it in #ifdef soup and causes your IDE to lie. Perhaps uint8_t - except on windows where it might be int for "backwards compatability". Or perhaps multiple definitions in different independent contexts (be that different namespaces or different included headers.) I've seen stupider shit frequently enough that the habit of spending more time performing a more thorough check than a simple hover will be time well spent. > you can pretty easily fix all of the users of a "Month" type Nah, that kind of underbaked leaky misabstraction isn't going to be properly thought out well enough to simply fix by merely poking at the type (and I'm confident in assuming it will be an underbaked leaky misabstraction on account of the lack of value provided.) You're going to have to actually audit all use of that type for dumb abstraction-breaking assumptions that make it brittle and fragile if you so much as sneeze on it's layout. A saving grace here: so little code will actually bother to use it, that it actually shouldn't be too terrible. Probably.
- CogitoCogito 4y ago> Multiply this experience by the number of all the layers in the code and maybe others might be able to appreciate why I prefer the second type of coding. Please someone tell me I am not the only one who feels this way! I don't understand the downvotes. You're expressing your opinion nothing else. That said, I generally agree with you. Layers of abstraction have a cognitive cost. That cost might be paid for by gains due to those abstractions, but the costs exist nonetheless. Whether this is bothersome is up to the programmer in question, but it is certainly a legitimate concern.