Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I make a private member object-encapsulated?

In Scala by typing private[this] we can make a private member only accessible by that particular object of the class, and another object of the same class cannot access it. If possible, how can we accomplish this in other languages... particularly F#, Erlang, and Clojure?

like image 575
codingXeno Avatar asked Oct 20 '16 00:10

codingXeno


People also ask

What are the two methods involved in encapsulation?

Encapsulation in OOP: getter/setter methods A getter method is used to retrieve the value of a specific variable within a class. A setter method is used to set or update the value of a specific variable within a class.

How can encapsulation be achieved?

How can Encapsulation be achieved? Explanation: Using access specifiers we can achieve encapsulation. Using this we can in turn implement data abstraction. It's not necessary that we only use private access.

Can methods be encapsulated?

In object‑oriented programming, you can limit access to an object's attributes and methods. The process of allowing only the object itself to have access to its attributes and methods is called encapsulation.


2 Answers

What you are asking about is basically the difference between Object-Oriented Data Abstraction and Data Abstraction with Abstract Data Types (ADTs, not to be confused with Algebraic Data Types, which are also often abbreviated as "ADT"). (See On Understanding Data Abstraction, Revisited by William R. Cook, his Proposal for Simplified, Modern Definitions of "Object" and "Object Oriented", and Object-Oriented Programming Versus Abstract Data Types.)

Two different instances of the same Abstract Data Type can inspect each other's representation (but not the representation of instances of other Abstract Data Types), whereas an object can never inspect another object's representation, even if the object is an instance of the same type. Cook identifies this property as the fundamental and defining characteristic of Object-Oriented Data Abstraction (and in fact Object-Orientation in general), and calls it Autognosis (Self-Knowledge). I prefer the term Xenoagnosis (Foreign-Not-Knowledge), which was applied by either Glenn Vanderburg, Rick de Natale, or the late Jim Weirich, IIRC, because it emphasizes the difference between the two approaches, namely that objects cannot know about other objects.

In Scala, as you noticed, Object-Oriented Data Abstraction can be achieved via the private[this] access modifier.

class TestPrivate {
  private       val notReallyPrivate = 42
  private[this] val privateIMeanIt   = 23

  def blowUp(other: TestPrivate) = {
    other.notReallyPrivate
    other.privateIMeanIt
  }
}
// error: value privateIMeanIt is not a member of TestPrivate

In Erlang, the equivalent to an Object is a Process, and Processes are fully encapsulated by the language semantics (they even have their own garbage-collected memory). They can even live on a different machine on a different continent, after all, so exposing representation is simply impractical. You send a message to a Process, and the Process is completely autonomous in its decision what to do with the message and how to respond to it. Everything is private[this], basically.

In Clojure, Object-Oriented Data Abstraction can be achieved via Closures. (This is true for pretty much any language with closures and higher-order subroutines, e.g. you can also use Functions as Objects in Erlang instead of Processes.) Think about ECMAScript: even though it confusingly has a language construct called "object", in ECMAScript Objects are actually implemented with Functions. You can implement them in Clojure the same way. The Object-Oriented Form of Data Abstraction is also called Procedural Data Abstraction, because instead of relying on Types, it relies on hiding the Data behind a Procedural Interface (or a Functional Interface, if you want, after all, Mutability and Side-Effects are orthogonal to OO). Cook argues (half-jokingly) that because λ-calculus has only functions, all abstraction is functional, and therefore λ-calculus is the first, oldest, and purest OO language – obviously this means that OO Data Abstraction must be possible in languages closely based on λ-calculus such as the Lisp family. (Historical anecdote: Scheme was originally designed to study OO and Actors, they only noticed during the implementation that the code paths of the interpreter for message sending and function calling were identical.)

In languages like Java or C♯, Object-Orientation is achieved using the Type System and Programmer Discipline, by only using interfaces as Types. interfaces cannot describe representation, therefore, as long as you make sure (through Programmer Discipline, Coding Styles, Code Reviews, maybe Static Analysis Tools) that only interfaces are ever used as Types (every local variable, field, method parameter, method return value, cast operator, instanceof check, and Generic Type Argument must always be an interface) and classes are only used as Factories (the only place where a class name is allowed to appear is directly next to the new keyword), then you have achieved Object-Oriented Data Abstraction. (There are some other caveats, most importantly that you must not use Reference Equality.)

interface ITestPrivate {
  default int thisIsPublic() { return 0; }

  default void blowUp1(ITestPrivate other) {
    other.thisIsPublic();
    other.privateIMeanIt();
  }
}

class TestPrivate implements ITestPrivate {
  private int privateIMeanIt() { return 23; }

  void blowUp2(TestPrivate other1, ITestPrivate other2) {
    other1.notReallyPrivate(); // works
    other2.notReallyPrivate(); // doesn't
  }
}

Doing this should also be possible in F♯, since F♯ supports interfaces as well.

like image 187
Jörg W Mittag Avatar answered Oct 24 '22 09:10

Jörg W Mittag


F# has public, internal and private access control specifiers. Scalas more restrictive private[this] which limits access to an instance of a type and not the type itself is not available as a specifier, so even private (most restrictive in F#) members of another instance can be accessed. class let bindings are instance-private though:

type Foo(x) =
    let totallyPrivate = x
    member private(*[this]*) __.Bar = x
    member this.Baz (other : Foo) =
        // other.Bar not accessible in Scala
        this.Bar + other.Bar + totallyPrivate
        // the field, constructor or member 'totallyPrivate' is not defined
        // other.totallyPrivate
like image 3
CaringDev Avatar answered Oct 24 '22 09:10

CaringDev