There are so many definitions of OOP out there, varying between different books, documentation and articles.

What really defines OOP?

  • Kache@lemm.ee
    link
    fedilink
    arrow-up
    32
    ·
    edit-2
    10 months ago

    You’re getting a lot of conceptual definitions, but mechanically, it’s just:

    keeping state (data) and behavior (functions) that operate on that state, together

    At minimum, that’s it. All the other things (encapsulation, message passing, inheritance, etc) are for solidifying that concept further or for extending the paradigm with features.

    For example, you can express OOP semantics without OOP syntax:

    foo_dict.add(key, val)  # OOP syntax
    
    dict_add(foo_dict, key, val)  # OOP semantics
    
    • Pipoca@lemmy.world
      link
      fedilink
      arrow-up
      6
      ·
      9 months ago

      keeping state (data) and behavior (functions) that operate on that state, together

      Importantly, that’s “together at runtime”, not in terms of code organization. One of the important things about an object is that it has dynamic dispatch. Your object is a pointer both to the data itself and to the implementation that works on that data.

      There’s a similar idea that’s a bit different that you see in Haskell, Scala, and Rust - what Haskell calls type classes. Rust gives it a veneer of OO syntax, but the semantics themselves are interestingly different.

      In particular, the key of type classes is keeping data and behavior separate. The language itself is responsible for automagically passing in the behavior.

      So in Scala, you could do something like

      def sum[A](values: List[A])(implicit numDict: Num[A]) = values.fold(numDict.+)(numDict.zero)
      

      Or

      def sum[A: Num](values: List[A]) = values.fold(_ + _)(zero)
      

      Given a Num typeclass that encapsulates numeric operations. There’s a few important differences:

      1. All of the items of that list have to be the same type of number - they’re all Ints or all Doubles or something

      2. It’s a list of primitive numbers and the implementation is kept separate - no need for boxing and unboxing.

      3. Even if that list is empty, you still have access to the implementation, so you can return a type-appropriate zero value

      4. Generic types can conditionally implement a typeclass. For example, you can make an Eq instance for List[A] if A has an Eq instance. So you can compare List[Int] for equality, but not List[Int => Int].