OOP is TRWTF



  • @Mason_Wheeler Good luck fixing bugs and unit testing when you have to do all the changes up front in a weakly typed language...


  • Considered Harmful

    @Mason_Wheeler said in OOP is TRWTF:

    @Captain said in OOP is TRWTF:

    @Mason_Wheeler said in OOP is TRWTF:

    ...and being able to play clever tricks like that is worth giving up the massive benefits of Liskov substitution?

    Liskov is a weak substitionality criterion...

    And yet it's conquered the world.

    The bottom line is, if OOP didn't work, it wouldn't work. People wouldn't have used it to conquer complexity and build the software we rely on every day. But they did, because it does, and nothing else does so nearly as well. And the main "secret ingredient" that makes it so good is the combination of inheritance, polymorphism, and virtual methods (AKA Liskov substitution).

    C is fucking broken in a million ways and every single feature is a foot-gun. It is horrible for every task. And yet people used it to conquer complexity and build the software we rely on every day. This is the 'Wikipedia is written in PHP' argument.



  • @Captain said in OOP is TRWTF:

    @Mason_Wheeler Good luck fixing bugs and unit testing when you have to do all the changes up front in a weakly typed language...

    Which is why you don't use them. How many class-based OOP languages are weakly typed? Python kinda-sorta-maybe if you squint hard enough... anything else?



  • @Mason_Wheeler most of them are weakly typed. C# is the strongest I know of, and it's far behind System F, let alone System F omega.

    But you're RIGHT for once. I don't use them.



  • @Captain If you think that, you have a very strange notion of typing discipline.



  • @Mason_Wheeler nah. Your wimpy type systems suck.


  • ♿ (Parody)

    @Mason_Wheeler said in OOP is TRWTF:

    This is literally the opposite of the truth. You appear to be completely unfamiliar with the TCIYF principle.

    What do space cock invaders have to do with anything?

    https://youtu.be/BpniqsaLw9w



  • @boomzilla I was impressed by the Afropunk too. :-)


  • Considered Harmful

    @Mason_Wheeler said in OOP is TRWTF:

    ...says the guy advocating for throwing out the most powerful programming technique we've managed to develop in favor of a mess of mixins and containers everywhere!

    It's the same concept, dumbass.
    In my hypothetical Dart-Kotlin hybrid,

    class B {
        fun c() {}
    }
    class A extends B {
        override fun c() {
            println("c")
            super.c()
        }
    }
    

    is exactly the same, semantically, as

    class B {
        fun c() {}
    }
    class A implements B by b {
        private var b = B()
        override fun c() {
            println("c")
            b.c()
        }
    }
    

    The only difference is, in the second example, 'subclasses' can ignore broken aspects of the superclass's implementation, and there is no worry that the class was not designed to be extended.


  • Considered Harmful

    @Captain said in OOP is TRWTF:

    @Mason_Wheeler nah. Your wimpy type systems suck.

    Care to, y'know, explain what the fuck it is you're talking about?



  • @pie_flavor Not right before lunch. 😉



  • @pie_flavor said in OOP is TRWTF:

    @Captain said in OOP is TRWTF:

    @Mason_Wheeler nah. Your wimpy type systems suck.

    Care to, y'know, explain what the fuck it is you're talking about?

    Most type systems are weak in that they allow for translation via some sort of autoboxing, or ints being put into longs and so on.


  • Banned

    @Mason_Wheeler said in OOP is TRWTF:

    @Gąska said in OOP is TRWTF:

    It makes it easier to refactor when it turns out they don't inherently belong together after all.

    No, it really doesn't. It makes it that much more difficult.

    Wut? How the hell can moving an independent component ever be harder than moving a dependent component?

    You're asking the wrong question. You shouldn't ask what's the point of decoupling. You should ask what's the point of coupling. Because coupling doesn't help you in any way and only causes things to be harder to untangle when need arises.

    This is literally the opposite of the truth.

    Is it? Then tell me, what exactly are you losing when you decouple, except a tiny bit of time?

    You appear to be completely unfamiliar with the TCIYF principle.

    Yes, I am. And I really could use some help in decrypting that acronym.

    2d231bd3-768f-46f0-8123-358d6a6bf617-image.png

    85858b19-7952-4d6c-8409-f3a18d5be7b4-image.png

    I'm gonna take a blind guess that "The Cum In Your Face" isn't it.

    When you have decoupled, more or less autonomous units, then taking one of these units and adapting it somewhere else is much easier than if they were more tightly coupled, because you'd have to start with decoupling them.

    Nothing is truly autonomous in a system of non-trivial complexity.

    Autonomous in the sense it doesn't have any dependencies other than its defined interface, and its defined interface can be rewired at will to whatever you want at the moment. As opposed to static references that cannot be changed other than by changing the implementation of the relevant module.

    "More-or-less autonomous" units tend to be less than you think.

    It's exactly as autonomous as I think it is. It's exactly as autonomous as I made it. It has defined inputs that I can put anything I want in. It has defined outputs that I can do whatever I want with. And the inside is full of interdependent parts but that's okay because it's small enough and it's a single functional unit that does one thing and one thing only.

    When they're explicitly coupled, and then you have to change something, it's explicit what you have to change. When they aren't, you still have to change the same things, but now they're harder to find.

    I don't know what mixins you're using, but in the ones I use, all the external dependencies are explicit. It's the entire point of a mixin to make them explicit, so they don't surprise you. If your mixins are implicitly coupled, you're doing something seriously wrong.

    Yes really. Breaking class hierarchies is one of the hardest refactoring tasks.

    Only when it's something that shouldn't be broken up in the first place.

    No, it's exactly as hard regardless of whether it should be broken up or not. Unless you mean that once you make a hierarchy, you should never ever ever alter that hierarchy? Then good luck refactoring anything.

    Extreme decoupling

    Who said anything about extreme decoupling? I never suggested anything like that. Don't put words in my mouth. I was just saying that it's good to avoid class inheritance wherever possible.

    That is extreme decoupling.

    I have no words for you. Your idiocy is without bounds.

    It's not an equivalent. Not even close. It just accomplishes the same task. In a completely different way that doesn't share even a single problem that multiple inheritance has.

    It has its own problems.

    Of course it does. But they pale in comparison with inheritance. Even single inheritance (most of the time; there are some use cases where single inheritance is actually better, but they're few and far between).

    Like, what if you're supposed to perform an operation that returns a value? With multiple inheritance, you can have trouble figuring out which one to call; with multiple mixins, you can just call all of them... and then you have trouble figuring out which return value is the one you want.

    That doesn't even make sense you retard 😖 Seriously, what are you even talking about? Why would having multiple mixins even be a problem? No, you won't call all of them. In the most basic scenario, you wouldn't call any of them. Because it's the container object that implements the interface, not the mixin. It's the method in the container object that decides which of the multiple mixins to call, or if to call any at all. Your scenario doesn't make sense at all. None of that could ever happen.

    How about you read up on the thing you're talking about before saying anything? Because it's pretty clear you have no fucking clue about what you're talking about.

    ...says the guy advocating for throwing out the most powerful programming technique we've managed to develop in favor of a mess of mixins and containers everywhere!

    Sounds like you still have no fucking clue what a mixin is.

    all the implicit contracts that must hold but are rarely documented

    Oh look, more vague hand-waving.

    Tell me, if I override this method, am I allowed to call super method from it? Am I allowed not to call it? Are there any restrictions on what I'm allowed to do before calling the super method?

    None of the millions of methods in any class in the entire .Net Framework, nor in the entire Java standard library, has these documented. They're like the most basic questions one might have when inheriting a library class. And yet nobody documents them.

    Because mixins have explicit interfaces defined in the language itself (so you must get them right or your code won't compile), and because mixins are isolated from the container except for the interface (so you don't even have the opportunity to do the wrong thing).

    And of course there are never any undocumented/poorly-documented internal details that turn out to be crucial to the proper usage of a mixin. :rolleyes:

    Of course there are. But 99% of what you need to know is in the signature. It's like functions. Of course there are cases where calling foo() requires calling bar() at least two real time seconds before and nobody documented it anywhere. But in 99% of cases, names and types of arguments and a short description of what the function does is all you need.

    It's not about preventing 100% of problems. That would be impossible. It's about preventing 99% of problems.

    THAT'S THE THING. THE CONTAINER ISN'T THE MIXIN. THEY AREN'T BOUND BY THE MIXIN INTERFACE. LSP DOESN'T APPLY BECAUSE YOU CANNOT PASS THE CONTAINER AS ARGUMENT WHERE THE MIXIN IS EXPECTED (unless the callee expects an interface and both the mixin and the container implement that interface but then you explicitly declared your container to implement that interface so of course it must implement it correctly; but you can choose not to implement the interface and it's fine and you still get all the other benefits of mixins, such as code reuse.)

    ...where are you getting "container" from?

    For lack of the better word for the object that uses the mixin. As an analogy to a derived class that uses base class. If you use a mixin, you don't necessarily adopt it's interface. If you derive a base class, you do necessarily use its interface. That's a big deal and it significantly reduces flexibility.

    If you knew what a mixin is, you'd know what I mean by container. There isn't anything else I could possibly mean by a container. That you didn't pick it up is a clear indication you have no fucking clue what mixins are and what this discussion is about.

    Sorry, I wasn't clear enough. All of the above are language-independent issue that appear everywhere you have class inheritance.

    More vague hand-waving. People always talk about the supposed "virtual methods in constructors problem", but they never give any real-world examples of it.

    Base class calls overridable method in constructor. Derived class overrides that method and uses a new field inside because it makes perfect sense when it's called by external code outside constructor. The constructor of derived class starts with super();. Boom.

    Occasionally they'll accompany it with a super-contrived example that looks nothing like real-world code, but it's something I've never actually run into, not even once, in 20 years of doing OOP.

    I've actually been in the exact scenario described above. I derived a library class that the docs told me to derive, but it never said that particular method is used during construction. And starting the constructor with super(); is a pretty common idiom, wouldn't you agree?


  • BINNED

    @Gąska said in OOP is TRWTF:

    I'm gonna take a blind guess that "The Cum In Your Face" isn't it.

    Wow, compared to that I was really close.


  • Considered Harmful

    @Gąska said in OOP is TRWTF:

    Tell me, if I override this method, am I allowed to call super method from it? Am I allowed not to call it? Are there any restrictions on what I'm allowed to do before calling the super method?
    None of the millions of methods in any class in the entire .Net Framework, nor in the entire Java standard library, has these documented. They're like the most basic questions one might have when inheriting a library class. And yet nobody documents them.

    https://i.imgur.com/RrCCIEw.png
    @discobot define for @Gąska literate


  • 🔀

    @gąska literate : (adjective) (of a person) able to read and write.

    Full definition:
    1. Adjective
      1. (of a person) able to read and write.
        1. having or showing education or knowledge, typically in a specified area
    2. Noun
      1. a literate person.

    View on lexico.com

  • Banned

    @pie_flavor said in OOP is TRWTF:

    @Gąska said in OOP is TRWTF:

    Tell me, if I override this method, am I allowed to call super method from it? Am I allowed not to call it? Are there any restrictions on what I'm allowed to do before calling the super method?
    None of the millions of methods in any class in the entire .Net Framework, nor in the entire Java standard library, has these documented. They're like the most basic questions one might have when inheriting a library class. And yet nobody documents them.

    https://i.imgur.com/RrCCIEw.png

    Does "be sure" mean MUST or SHOULD? And should the call be made before or after I do my work? What if I DON'T want delegates to receive the event - am I allowed to do that? What methods I'm allowed to call between having my OnPaint() called and calling the base OnPaint? What methods I'm allowed to call between the base OnPaint() returning and my OnPaint returning?

    Those docs don't even start to answer the questions I asked.


  • ♿ (Parody)

    @Gąska it pretty clearly says, "must" to me. It's silent on the other bit.


  • Considered Harmful

    @Gąska base.OnPaint calls the paint event for registered listeners. If you need to do stuff after that happens, you can. If you need to do stuff before that happens, you can. If you want to make it not happen at all, you can. If there was something that needed to be documented, it'd be documented.


  • Discourse touched me in a no-no place

    @Gąska said in OOP is TRWTF:

    Does "be sure" mean MUST or SHOULD?

    Must.

    @Gąska said in OOP is TRWTF:

    And should the call be made before or after I do my work? What if I DON'T want delegates to receive the event - am I allowed to do that? What methods I'm allowed to call between having my OnPaint() called and calling the base OnPaint? What methods I'm allowed to call between the base OnPaint() returning and my OnPaint returning?

    Assume that (in this context) if the documentation doesn't tell you the rules, that it's up to you and depends on what you're doing.


  • Banned

    @pie_flavor said in OOP is TRWTF:

    If you want to make it not happen at all, you can.

    This directly contradicts the documentation. At least if we assume @boomzilla is correct in his interpretation.

    OnPaint() gets called for a reason. The caller expects some things to happen. If you override that method, you better be damn sure you all the explicit and implicit invariants about the method's behavior still hold. Getting it wrong may cause - and has caused - bad things to happen.


  • Banned

    @loopback0 said in OOP is TRWTF:

    @Gąska said in OOP is TRWTF:

    And should the call be made before or after I do my work? What if I DON'T want delegates to receive the event - am I allowed to do that? What methods I'm allowed to call between having my OnPaint() called and calling the base OnPaint? What methods I'm allowed to call between the base OnPaint() returning and my OnPaint returning?

    Assume that (in this context) if the documentation doesn't tell you the rules, that it's up to you and depends on what you're doing.

    As I said - guesswork.


  • Considered Harmful

    @Gąska It doesn't say whether you should jump around in a circle either. The horrors of incomplete documentation.


  • ♿ (Parody)

    @Gąska said in OOP is TRWTF:

    @pie_flavor said in OOP is TRWTF:

    If you want to make it not happen at all, you can.

    This directly contradicts the documentation. At least if we assume @boomzilla is correct in his interpretation.

    OnPaint() gets called for a reason. The caller expects some things to happen. If you override that method, you better be damn sure you all the explicit and implicit invariants about the method's behavior still hold. Getting it wrong may cause - and has caused - bad things to happen.

    I guess that it does tack on that "if" so maybe it isn't must. Poorly written. :mlp_shrug:



  • @pie_flavor Nobody is wondering whether they need to jump in a circle to inherit from that class...


  • BINNED

    @boomzilla said in OOP is TRWTF:

    @Gąska said in OOP is TRWTF:

    @pie_flavor said in OOP is TRWTF:

    If you want to make it not happen at all, you can.

    This directly contradicts the documentation. At least if we assume @boomzilla is correct in his interpretation.

    OnPaint() gets called for a reason. The caller expects some things to happen. If you override that method, you better be damn sure you all the explicit and implicit invariants about the method's behavior still hold. Getting it wrong may cause - and has caused - bad things to happen.

    I guess that it does tack on that "if" so maybe it isn't must. Poorly written. :mlp_shrug:

    It says "be sure ... so that registered delegates receive that event".
    To me, this reads like you MUST call it if you want things to function normally, like stuff getting painted. If you don't, you won't be breaking any invariants, but stuff won't get painted. Which sounds unlikely to be what you want, but :mlp_shrug:


  • Banned

    @topspin I see a lot of reading between lines in your post. You are most likely correct, but that correctness cannot be checked by reading up documentation.

    "When you call OnPaint(), the Paint event gets sent" sounds like an invariant to me. Even if things appear to work when you break this invariant, it doesn't necessarily mean your code is correct. But it doesn't mean it's incorrect. It might not be an invariant, even though the documentation suggests it is invariant. But it doesn't outright state it is, so really, all bets are off. And that's how it is 99% of the time whenever you overload non-abstract method - you either read up sources (which might or might not be possible), or cross your fingers and hope you've done your divination rituals right and your intuition is correct.

    Or you could use the mixin pattern and you wouldn't even be physically able to do the wrong thing (unless you abuse the type system to the point of absurdity), just like you aren't physically able to pass arguments of a wrong type to a function (unless you abuse the type system to the point of absurdity).



  • @pie_flavor said in OOP is TRWTF:

    C is fucking broken in a million ways and every single feature is a foot-gun. It is horrible for every task.

    It's good for discouraging people from ever picking up programming in their life 🍹


  • Banned

    @_P_ in my experience, most of the time, any language will do.



  • @Gąska said in OOP is TRWTF:

    @_P_ in my experience, most of the time, any language will do.

    Interesting, so either Scratch isn't a language, or you're trying to say Scratch is like that too and be ready to be beaten up by a billion "coding educators" 🚎



  • @pie_flavor said in OOP is TRWTF:

    @Captain said in OOP is TRWTF:

    @Mason_Wheeler nah. Your wimpy type systems suck.

    Care to, y'know, explain what the fuck it is you're talking about?

    Wimpy type system aren't good enough to check for possible inhabited values of a type, not to mention computing a type using type-level functions at compile time (dependent types) or impose a certain structure in your program/your usage of API others have made (category theory-inspired typeclasses in Haskell). TypeScript has a more powerful type system than Java/C# in that you have union/intersection types, but is still mostly a toy type system. For kids who want to think they know their way around types.

    Also, unpopular opinion, but enum is a ugly hack due to wimpy type systems. The type system should be able to let me define that TRWTFBoolString can only be "true" or "False", and emit a type error at compile time when I write TRWTFBoolString myBool = "FILE_NOT_FOUND" instead of TRWTFBoolString myBool = "true". In TypeScript you can actually define a type like LessOrEqual<n extends Number> which can only be assigned integers from 0 to n, so that LessOrEqual<5> num = 1 typechecks while LessOrEqual<5> num = 6 emits a type error.



  • @_P_ said in OOP is TRWTF:

    @Gąska said in OOP is TRWTF:

    @_P_ in my experience, most of the time, any language will do.

    Interesting, so either Scratch isn't a language, or you're trying to say Scratch is like that too and be ready to be beaten up by a billion "coding educators" 🚎

    Any of those things could be true, other than the implication that a billion people, or even one person, have ever heard of Scratch



  • @hungrier said in OOP is TRWTF:

    @_P_ said in OOP is TRWTF:

    @Gąska said in OOP is TRWTF:

    @_P_ in my experience, most of the time, any language will do.

    Interesting, so either Scratch isn't a language, or you're trying to say Scratch is like that too and be ready to be beaten up by a billion "coding educators" 🚎

    Any of those things could be true, other than the implication that a billion people, or even one person, have ever heard of Scratch, because that'd be a Stretch

    FTFY


  • Banned

    @hungrier said in OOP is TRWTF:

    @_P_ said in OOP is TRWTF:

    @Gąska said in OOP is TRWTF:

    @_P_ in my experience, most of the time, any language will do.

    Interesting, so either Scratch isn't a language, or you're trying to say Scratch is like that too and be ready to be beaten up by a billion "coding educators" 🚎

    Any of those things could be true, other than the implication that a billion people, or even one person, have ever heard of Scratch

    I heard of Scratch!

    684b095b-f903-424e-b3b3-1231fabb65e1-image.png


  • Considered Harmful

    @Carnage said in OOP is TRWTF:

    @pie_flavor said in OOP is TRWTF:

    @Captain said in OOP is TRWTF:

    @Mason_Wheeler nah. Your wimpy type systems suck.

    Care to, y'know, explain what the fuck it is you're talking about?

    Most type systems are weak in that they allow for translation via some sort of autoboxing, or ints being put into longs and so on.

    Yeah, but then he insinuated that C# was one of those.



  • @pie_flavor said in OOP is TRWTF:

    @Carnage said in OOP is TRWTF:

    @pie_flavor said in OOP is TRWTF:

    @Captain said in OOP is TRWTF:

    @Mason_Wheeler nah. Your wimpy type systems suck.

    Care to, y'know, explain what the fuck it is you're talking about?

    Most type systems are weak in that they allow for translation via some sort of autoboxing, or ints being put into longs and so on.

    Yeah, but then he insinuated that C# was one of those.

    Auto-boxing is another completely separate issue, and I think you need to blame them trying to copy C++ syntax for that. Either everything is raw data type like C, or everything is an object like, I dunno, Ruby. They shouldn't be mixed together.


  • Considered Harmful

    @_P_ What's wrong with autoboxing? The types aren't malleable, they're still the same types, they can just convert between each other without explicit casts.


  • BINNED

    @Gąska said in OOP is TRWTF:

    @topspin I see a lot of reading between lines in your post. You are most likely correct, but that correctness cannot be checked by reading up documentation.

    "When you call OnPaint(), the Paint event gets sent" sounds like an invariant to me. Even if things appear to work when you break this invariant, it doesn't necessarily mean your code is correct. But it doesn't mean it's incorrect. It might not be an invariant, even though the documentation suggests it is invariant. But it doesn't outright state it is, so really, all bets are off. And that's how it is 99% of the time whenever you overload non-abstract method - you either read up sources (which might or might not be possible), or cross your fingers and hope you've done your divination rituals right and your intuition is correct.

    Even more reading between the lines, my (maybe wrong) assumption was that you don't call OnPaint() at all yourself, but the system creates those calls for you "spontaneously". Why this matters in this scenario: You have no expectation of when / if any of these events get created, so it is unlikely that you can have bugs when none are created. (Not impossible, though. You could do some start-up initialization the first time it happens, and then you're screwed. There's probably a Raymond Chen article about something like that on WM_PAINT).
    If that's not the case, you're correct that things can go wrong when not implemented as expected.

    Or you could use the mixin pattern and you wouldn't even be physically able to do the wrong thing (unless you abuse the type system to the point of absurdity), just like you aren't physically able to pass arguments of a wrong type to a function (unless you abuse the type system to the point of absurdity).

    I'm not exactly sure how your mixin-based system would look like and how the type system would prevent this, but you can achieve the same thing with OOP:
    Use the non-virtual interface idiom, where there is a public non-virtual base-method that does all the pre- and post-processing things to guarantee your invariants, and only for the things you're actually supposed to customize calls a private virtual method.

    To wit:

    class Base
    {
      std::vector<Delegate*> delegates;
      virtual void handleOnPaint() {}
    public:
      void onPaint() {
        for (auto d : delegates)
          d->doThatDelegateThing(...)
        handleOnPaint();
        // throw in some post-processing here for good measure...
      }
    }
    
    class Derived : public Base
    {
    public:
      void handleOnPaint() override {
        std::cout << "Custom implementation here.\n";
      }
    }
    

  • Discourse touched me in a no-no place

    @_P_ said in OOP is TRWTF:

    In TypeScript you can actually define a type like LessOrEqual<n extends Number> which can only be assigned integers from 0 to n, so that LessOrEqual<5> num = 1 typechecks while LessOrEqual<5> num = 6 emits a type error.

    I believe Ada (or one of its closely related languages) had types like that. They were a total ball-ache to use in practice because they effectively don't support arithmetic operations. Or rather they do… but the results are not of the restricted type so you end up having to do contortions to use the values. Really truly horrible. I'm so glad that I've done nothing with that stuff in the past two decades. Instead, you end up with the compiler (and other static analysers) having such facts, but just as derived information rather than in the language's type system, which seems saner in practice.

    (Java's enums are better; they don't even pretend to be arithmetic types and are saner for it.)


  • Discourse touched me in a no-no place

    @pie_flavor said in OOP is TRWTF:

    What's wrong with autoboxing?

    In practical terms, the problem with boxed values is that they're a lot more expensive to use than unboxed ones, both in terms of time and space. The problem with autoboxing is therefore that it can hide major performance trouble in your code.



  • @dkf said in OOP is TRWTF:

    @_P_ said in OOP is TRWTF:

    In TypeScript you can actually define a type like LessOrEqual<n extends Number> which can only be assigned integers from 0 to n, so that LessOrEqual<5> num = 1 typechecks while LessOrEqual<5> num = 6 emits a type error.

    I believe Ada (or one of its closely related languages) had types like that. They were a total ball-ache to use in practice because they effectively don't support arithmetic operations. Or rather they do… but the results are not of the restricted type so you end up having to do contortions to use the values. Really truly horrible. I'm so glad that I've done nothing with that stuff in the past two decades. Instead, you end up with the compiler (and other static analysers) having such facts, but just as derived information rather than in the language's type system, which seems saner in practice.

    (Java's enums are better; they don't even pretend to be arithmetic types and are saner for it.)

    Sounds like it's more that you're a victim of bad implementation than it's a bad idea overall.


  • Discourse touched me in a no-no place

    @_P_ said in OOP is TRWTF:

    Sounds like it's more that you're a victim of bad implementation than it's a bad idea overall.

    No. The idea is Bad in itself, and those who promulgate it are bad for pushing something they've never tried to do in practice and should feel bad about their pathetic little lives for the waste of effort they're encouraging others to do. It's all the very worst sort of type wanking.


  • BINNED

    @dkf said in OOP is TRWTF:

    @_P_ said in OOP is TRWTF:

    In TypeScript you can actually define a type like LessOrEqual<n extends Number> which can only be assigned integers from 0 to n, so that LessOrEqual<5> num = 1 typechecks while LessOrEqual<5> num = 6 emits a type error.

    I believe Ada (or one of its closely related languages) had types like that. They were a total ball-ache to use in practice because they effectively don't support arithmetic operations. Or rather they do… but the results are not of the restricted type so you end up having to do contortions to use the values. Really truly horrible. I'm so glad that I've done nothing with that stuff in the past two decades. Instead, you end up with the compiler (and other static analysers) having such facts, but just as derived information rather than in the language's type system, which seems saner in practice.

    (Java's enums are better; they don't even pretend to be arithmetic types and are saner for it.)

    I already hate unsigned values for that reason: you can't sensibly do arithmetic on them (without casting back and forth).
    Given the choice, I prefer to only use unsigned values when modulo arithmetic is required or a file/network data structure requires that. "Oh, but counts/sizes should always be positive". Yeah, but sometimes you want to do arithmetic on that too, and then you end up with c = abs(a-b) doing the wrong thing even though all variables involved are positive.


  • Discourse touched me in a no-no place

    @topspin said in OOP is TRWTF:

    I already hate unsigned values for that reason

    It's usually best to work either totally with signed values or totally with unsigned values (they have different operations). Mixing the two leads to frustration and casting, and that leads to the Dark Side.


  • BINNED

    @dkf said in OOP is TRWTF:

    @topspin said in OOP is TRWTF:

    I already hate unsigned values for that reason

    It's usually best to work either totally with signed values or totally with unsigned values (they have different operations). Mixing the two leads to frustration and casting, and that leads to the Dark Side.

    True. And since I can't think of a scenario where you don't deal with any signed values at all, I prefer only dealing with signed values.
    A requirement for modulo arithmetic seems to be the rare edge case compared to normal arithmetic.



  • @pie_flavor said in OOP is TRWTF:

    @_P_ What's wrong with autoboxing? The types aren't malleable, they're still the same types, they can just convert between each other without explicit casts.

    But then how do you know what operation you will be doing on the data?
    Given "3" + 0.14 will I get 3.14 or "30.14"?
    Is it clear without reading documentation or memorizing the autoboxing conversion hierarchy?


  • Banned

    @djls45 that's not autoboxing you're talking about.



  • @Gąska said in OOP is TRWTF:

    unless you abuse the type system to the point of absurdity

    The Enlightened thread is :arrows:


  • ♿ (Parody)

    @djls45 said in OOP is TRWTF:

    @pie_flavor said in OOP is TRWTF:

    @_P_ What's wrong with autoboxing? The types aren't malleable, they're still the same types, they can just convert between each other without explicit casts.

    But then how do you know what operation you will be doing on the data?
    Given "3" + 0.14 will I get 3.14 or "30.14"?
    Is it clear without reading documentation or memorizing the autoboxing conversion hierarchy?

    Autoboxing is (in java) converting an int to an Integer.



  • @Gąska said in OOP is TRWTF:

    @djls45 that's not autoboxing you're talking about.

    @boomzilla said in OOP is TRWTF:

    @djls45 said in OOP is TRWTF:

    @pie_flavor said in OOP is TRWTF:

    @_P_ What's wrong with autoboxing? The types aren't malleable, they're still the same types, they can just convert between each other without explicit casts.

    But then how do you know what operation you will be doing on the data?
    Given "3" + 0.14 will I get 3.14 or "30.14"?
    Is it clear without reading documentation or memorizing the autoboxing conversion hierarchy?

    Autoboxing is (in java) converting an int to an Integer.

    Oh, right. :derp:


Log in to reply