In my last blog post, I told you that Grace was growing, and talked a little about GUnit. It’s also growing in other ways: a type checker is under development, and the Graphics libraries now run in the Javascript (Web -based) implementation, as well as in the C implementation.
But notice that all of these developments have been in the libraries. The core of the language has actually shrunk, in at least one way.
We took private fields out of objects.
Before you get too excited, and start calling us up and asking how we expect you to teach about encapsulation if objects can’t have private fields, let me tell you that Grace still does have confidential fields and methods. Confidential attributes are those that can be accessed by an object and by those objects that inherit from it, and confidential is the default for fields (as it is, for example, in Smalltalk).
As originally proposed, private was a bit odd, because it applied only to fields: there were no private methods. Private meant just that: accessible only to the object that contained it, and to no others. It added complexity: three levels of protection are certainly more to explain than two. Moreover, we realized, that because of Grace’s lexical scope, it didn’t really add any power. This is because the effect of a private field can be obtained by removing the field from the object entirely, and putting it in an enclosing scope.
The best way to illustrate this is with an example. Here is a simple one, inspired by a problem in Mark Miller’s dissertation. The problem is to create a counter, and two objects that access it: one object can be used only to increment the counter, and the other only to decrement it.
method counterPair {
var counter:Number := 0
def countUp = object {
method inc { counter := counter + 1 }
method value { counter }
}
def countDown = object {
method dec { counter := counter - 1 }
method value { counter }
}
object { method up { countUp }; method down { countDown } }
}
def c1 = counterPair
def c2 = counterPair
c1.up.inc
c1.up.inc
print "counter 1 has value {c1.up.value}"
print "counter 2 has value {c2.up.value}"
c2.down.dec
c2.down.dec
print "counter 1 has value {c1.up.value}"
print "counter 2 has value {c2.up.value}"
counterPair
makes the two objects, one for incrementing and one for decrementing counter
. Because we don’t have tuples built-in, counterPair
returns an object with two attributes, up
and down
, which are the increment and decrement objects. The example code creates two counters, and the output shows that they are independent.
You can run this code yourself here.
The default protection for methods is public, because that is what one wants most of the time; a method can be made confidential by adding the “is confidential” annotation. The default protection for fields is confidential, but a field can be made readable, and, in the case of a variable field, writeable, with the is readable and is writable annotations. To the requestor, accessing a readable or writable field looks exactly like reading from or assigning to a variable, as it always has. So, the line of code above that constructs the result of counterPair
could have been written
object { def up is readable = countUp; def down is readable = countDown }
;
in both cases the client object can request the up and down attributes of the newly-created object.