How can I enclose a closed object-object?

In Scala, by typing private[this] , we can make a private member available only to this particular class object, and another object of the same class will not be able to access it. If possible, how can this be done in other languages ​​... especially F #, Erlang and Clojure?

+6
source share
4 answers

What you're asking is basically the difference between an 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 About the understanding of data abstraction, revised 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 verify each other's representation (but not the presentation of instances of other abstract data types), while an object can never check another representation of an object, even if the object is an instance of the same type. Cook identifies this property as the fundamental and defining characteristic of an object-oriented data abstraction (and in fact, object-oriented in general) and calls it Autognosis (Self-Knowledge). I prefer the term Xenoagnosis (Foreign-Not-Knowledge), which was used either by 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 have noticed, object-oriented data abstraction can be achieved using 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 of Object is a process, and processes are fully encapsulated by the semantics of the language (they even have their own assembly, collected using garbage). In the end, they can even live on another machine on another continent, so stripping a performance is simply impractical. You send a message to the process, and the process is completely autonomous in deciding what to do with the message and how to respond to it. Mostly private[this] .

In Clojure, object-oriented data abstraction can be achieved through Closures. (This is true for almost any language with closures and higher order routines, for example, you can also use functions as objects in Erlang instead of processes). Think about ECMAScript: although it vaguely has a language constructor called an “object”, ECMAScript objects are actually implemented using functions. You can implement them in Clojure the same way. An object-oriented form of data abstraction is also called procedural data abstraction, because instead of relying on types, it relies on hiding data behind a procedural interface (or a functional interface, if you want, after all, reciprocity and side effects are orthogonal to OO). Cook claims (half-jokingly) that since -calculus has only functions, the whole abstraction is functional, and therefore -calculus is the first, oldest and purest OO language - obviously, this means that the OO Data abstraction should be possible in languages ​​that are closely associated with a stone, such as the Lisp family. (Historical anecdote: The scheme was originally intended to study OO and Actors, they only noticed during implementation that the interpreter code paths for sending messages and calling functions were identical.)

In languages ​​such as Java or C♯, Object-Orientation is achieved using the Discovery Type System and Programmer, using only the interface as types. interface cannot describe the view, so if you make sure (through programming discipline, coding styles, code reviews, possibly static analysis tools) that only interface ever used as types (each local variable, field, method parameter, return value method, casting operator, instanceof checking, and the argument of the general type should always be interface ), and class es are used only as factories (the only place where it is allowed to display the class name to the new keyword), then you have reached the object-orientation data abstraction. (There are some other caveats; the most important thing is that you should not use referential 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 the interface as well .

+6
source

F # has public , internal and private access control specifiers . Scalas is a more restrictive private[this] , which restricts access to the type instance, not the type itself, is not available as a qualifier, so even private tags (the most restrictive in F #) of the other instance can be accessed. class let bindings are instances 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 
+3
source

Check out @JohnPalmer's comment as well as Access Control . What can be easily found using Google .

 type TestPrivate(x) = let y = x - 10 member private __.X() = 10 member __.Z() = x + 10 member __.Y = __.X() let test = TestPrivate(5) test.Z() //val it : int = 15 test.Y //val it : int = 10 test.X() 

error FS0491: the constructor of the element or object "X" is not available. Private members can only be granted permission with the type of ad. Protected elements can only be accessed from the expandable type and cannot be accessed from the internal lambda express ions.

Edit

 type TestPrivate(x:string) = let totallyPrivate = x member private __.PrivateX = x member __.FromOther(other:TestPrivate) = __.PrivateX + "-" + other.Y + "-" + other.PrivateX + "-" + totallyPrivate member __.Y = __.PrivateX let thisTest = TestPrivate("this") let otherTest = TestPrivate("other") thisTest.FromOther(otherTest) //val it : string = "this-other-other-this" otherTest.FromOther(thisTest) //val it : string = "other-this-this-other" thisTest.FromOther(thisTest) //val it : string = "this-this-this-this" otherTest.FromOther(otherTest) //val it : string = "other-other-other-other" 

Thus, an instance of this can, of course, access fully Private, but neither this nor other can access other relationships, although they can access another private property.

+2
source

As I said in a comment on @ JörgWMittag, I tend to agree with a comment by @SamEstep that what you want to do, codingXeno, rarely makes sense in Clojure. This is a nice exercise, however, in Common Lisp or Scheme, and is only slightly more complicated in Clojure. Here is a very simple illustration of something like what you requested:

 (def getit-and-setit (let [x (atom 42)] [(fn [] @x), (fn [new-x] (reset! x new-x))])) 

Here we defined a local variable x , the value of which is an "atom", that is, something that will contain a value that can be replaced with a new value. (The contents of an atom can be extracted using @ or deref . It can be replaced using reset! Or some other useful operator.) Then we return the two-element vector. Each item is a function. (Usually, a comma is not used, but it is resolved and makes this code more clear.)

 (def get-it (first getit-and-setit)) (def set-it! (second getit-and-setit)) 

The first function is extracted here and a new get-it variable is defined so that the function is its value. And we defined set-it! so that another function is its value.

get-it returns the contents of the atom, the value of x :

 (get-it) ;==> 42 

set-it! replaces the contents of the atom with a new value:

 (set-it! 5) (get-it) ;==> 5 

There is no way to access x , except through get-it and set-it! . x is "private".

The reason this would be easier in Common Lisp or Scheme is because in these languages ​​new local values ​​can be assigned to normal local variables. Clojure does not allow this, as a rule. (There are ways to change the values ​​of variables without using atoms in Clojure if you use Java or Javascript interaction methods. However, this is what you need to do only for the sake of interaction or for some other special need.)

On the other hand, if you want to restrict access to the components of the Clojure defrecord structure, I don’t think you can do it except using a method like the one above. You can do this with the associated deftype data deftype , but commonly used to interact with Java.

+1
source

Source: https://habr.com/ru/post/1011495/


All Articles