A number of languages support a kind of “public”/”protected”/”private” model. The actual implementation and meaning of this model vary from programming language to programming language, but the basic idea remains the same: preventing code outside of an object from modifying certain things inside of it.
As an interpreted language, Copper doesn’t support such a model.
Private vs public is a tricky aspect about languages. The Rust dev team, for a long time, couldn’t figure out what would qualify as “public” and what as “private”. Part of the problem was the fact that the language of Rust is not suitably designed for such a system. C++ is, but that’s because C++ promotes encapsulation (though even here, C++ has a problem). Rust allows for extending classes much like Ruby, which is a bizarre concept for a compiled language. As such, the definition of a “class” isn’t limited per file… or is it? For some time, they couldn’t decide. What if it’s public in one file and private in another and both files are imported into another file? More conflicts? How silly. And for what purpose? Supporting a paradigm?
C++ may have encapsulation, but there is still a similar problem as with Rust: what if an object D inherits from both class C and B (which inherit from A) but the implementations of the virtual members of A are different for B and C. The privacy/public problem can be solved by always inheriting A as public (otherwise you get the same predicament as Rust). But in the case where C and B have different implementations, you get an ambiguity that led to Bjarne Stroustrup (IMO sadly) suggesting universal function syntax – a non-solution that only creates a new problem to solve a different one.
The only real solution to the problems of C++ and Rust are better designs, but industry production speed is the enemy of that.
The creator of Ruby made a very odd move: He decided to support multiple ways of doing things. There are 3 ways to do just about anything in Ruby. While people working around copyright law might appreciate this, the bizarre syntax of Ruby has made its code some of the most unreadable garble I’ve ever had the unpleasant experience trying to read, which is odd considering that it’s basic syntax isn’t all that bad. (There are worse things for me than Ruby, yes, and fortunately, I haven’t needed to read them.) One of the many features of the language is the ability to create getters and setters in multiple ways: “reader”, “writer”, and “accessor”. Nevermind creating a getter and a setter function. The whole point was to aid the lazy while still enforcing the class encapsulation paradigm that hid all variables. If you count keystrokes, however, it’s almost as fast just making the getter and setter and then commenting out what you don’t need.
AngelScript allows for public, protected, and private variables. I assume this works similar to the C++ approach because AngelScript models itself after imperative languages. That said, imperative languages are a good model if what you have is static. I don’t.
Because the function-objects in Copper are meant to be extensible at-will, it makes no sense trying to enforce a public/protected/private system. That only forces more overhead and complicates the mental model. What would “private” even mean? Moreover, what benefit is there for such a system. I can’t think of any.
For a language like Copper, a privacy system isn’t necessary. Copper is not meant for creating giant applications where you need to somehow hide the details from everyone else. It’s meant more for people who are working on the entire program themselves and are already going to know what is meant to be accessed and what isn’t. Code comments are sufficient in that case.
Part of the idea of private members is that encapsulation prevents you from modifying a variable without going through the proper channel. Going through the proper channel allows for other action to be taken, such as range checking for arrays. It allows for compiler warnings when trying to access the data directly. This is nice for a language that doesn’t natively handle these things, such as C. The need for a setters seems to be non-existent in an interpreted language such as Copper where range checking can be done “behind the scenes”. However, there are more purposes than simply range-checking and performing trivial existence checks. Some unique logic may occur within a setter. Hence, using a setter rather than accessing a variable directly can be very beneficial. Notably, I would argue it’s also trivial to label something as meant to be set via setter, and indicating this can be automated in IDEs these days. What happens if you need to set something directly? In Copper, you can.
Copper uses an open play-ground policy that allows all variables to be accessible. In effect then, Copper is like LISP disguised as an imperative language. Go figure. On the bright side (haha, sorry LISP), it does make checking and analyzing variable contents much easier.