State Encapsulation - Spicery/Nutmeg GitHub Wiki

Overview - Basic Concept

A key design principle in object-oriented programming is state encapsulation. It is a key technique for stateful programming that prevents unintentional updates of shared store. But what does that actually mean in practice? This page explains what it is and how it works in Nutmeg.

The central concept is that the mutable state of an object X is 'owned' by X. The only way to modify that object's state is to have a handle on X. The simple interpretation of this is that if a mutable object Y is part of the state of X, it cannot be exposed by the public methods of X and is permanently hidden away.

For example, the following class exposes the slot (a.k.a. field) clubMembers and so its state is not encapsulated:

class Club:
    slot clubTitle
    slot clubMembers = MutableList()
    def ^addMember( m ): ^clubMembers.add( m ) end
    def ^countMembers(): ^clubMembers.length end
end

We can easily discover it is not encapsulated like this:

club := Club()
club.add( Member( 'Lucky Jim' ) )    ### class Member defined elsewhere
assert club.countMembers == 1        ### passes
mlist := club.clubMembers            ### get privileged access to the inner state of club!
mlist.clear()                        ### delete all the members of the list ... but this will damage the club membership!
assert club.countMembers == 1        ### FAILS - the club was altered without any direct access to the object, only its internals.

To encapsulate the state of the object we need to prevent the 'outside world' being able to access the clubMembers slot. It is not enough to limit the visibility of the clubMembers variable, although that is a good start, we also have to protect against 'reflection'. (N.B. Nutmeg provides a highly structured form of reflection that supports traceability and garbage collection and not the usual unstructured reflection.) The local modifier, when applied to the clubMembers slot, not only limits the visibility of clubMembers to the scope of the class but protectively marks the slot.

class Club:
    slot clubTitle
    local slot clubMembers = MutableList()
    def ^addMember( m ): ^clubMembers.add( m ) end
    def ^countMembers(): ^clubMembers.length end
end

Now our attempt to subvert the state of Club fails.

club := Club()
club.add( Member( 'Lucky Jim' ) )    ### class Member defined elsewhere
assert club.countMembers == 1        ### passes
mlist := club.clubMembers            ### get privileged access to the inner state of club!
### MISHAP:  Reference to unknown identifier 
### VARIABLE: clubMembers

Accommodating Dependencies

TO BE DONE

  • Key idea - store ownership
  • To make this work - explicit dependencies
  • RW views considered questionable - need to maintain relationship
  • Callables - must package the items they operate on