Brief summary:
I think we should make deep copies of presences when we're making new presences.
---->Issues to be decided: What context are handlers executed in? When will we be in root context?
Axioms/Assumptions:
1) Entities support multiple presences between worlds.
2) There will be one entity presence per world.
3) The (overwhelmingly) common use case will be a programmer scripting an entity with a single presence.
4) The most likely reasons one would have multiple presences would be to:
a) Bridge two worlds: Project content from one world into another world.
b) Simplify sharing state between presences.
5) Even for the above two scenarios, the amount of shared state between two presences should be small compared to the amount of independent state. For instance, consider an art gallery. The art gallery may want to share some data (eg. a list of which pieces are for sale), but it may not want to share the majority of other data. For example, what times the gallery closes; whom to contact in case of fire, flood, or theft; customer's addresses; etc. (Note: this point excludes functions. Both presences will likely share lots of functions.)
6) Presences in multiple spaces will want to be more similar to each other than different: if an entity is a store in Space A, it's not going to be a truck in Space B; if an entity is a duck in Space B, it's not going to be a computer in Space A.
Wants:
1) Want to make programs easy to reason about.
2) Want default case to hide presences.
3) Want to limit having to re-write or copy functions each time we create a new presence.
Suggestion:
1) Have a default presence.
2) When creating a new object in the root context (but when will we be in the root context?), the object will be copied to each presence. For instance:
-Before object creation:
root------List Object
/ \
/ \
P1 P2
-Object creation:
myList = List();
-After object creation:
root------List Object
/ \
/ \
P1 P2
| |
myList myList
3) One can specify context of object to change by prepending presence. For instance,
p1.myList.add(7)
adds the number 7 to the list under presence 1. If one does not prefix with desired presence, for instance,
myList.add(7)
then the operation will occur to the object in the default presence.
4) To specify an object that will be available globally, one should just need to do the following:
-Before global object creation:
root------List Object
/ \
/ \
P1 P2
-Object creation:
AddressBook = globvar Object(); //or root.AddressBook = Object();
root.AddressBook.listOfNames = List();
root.AddressBook.listOfAddressables = List();
-After object creation:
root------List Object
/ \ |____AddressBook---listOfNames
/ \ |______listOfAddressables
P1 P2
| |
myList myList
-To instantiate a separate copy for each presence:
mAddrBook = AddressBook();
root------List Object
/ \ |____AddressBook---listOfNames
/ \ |______listOfAddressables
/ \
mAddrBook----P1 P2---mAddrBook
| |
myList myList
5) Let's say that myList has the function messageSumElements, which sums all the elements in the list and then writes a message to all presences in its local address book with the sum of the elements in the list:
function messageSumElements()
{
//initialize counter
summer = 0;
//walk list and sum.
for s in myList:
summer += s;
//send individual message to all members of mAddrBook
for s in mAddrBook.listOfAddressables:
s.sendMessage(str(summer));
}
When you call
p1.myList.messageSumElements()
the function messageSumElements will execute within the context of p1. What this means is that the mAddrBook inside of messageSumElements will be p1's mAddrBook.
If you wanted to instead send messages to members of another presence's address book, you could re-define the messageSumElements function as follows:
//presArg is a presence argument that tells us where to select address book data from.
function messageSumElementsOthePres(presArg)
{
//initialize counter
summer = 0;
//walk list and sum.
for s in myList:
summer += s;
//send individual message to all members of mAddrBook
for s in presArg.mAddrBook.listOfAddressables:
s.sendMessage(str(summer));
}
And to call it, you would say:
p1.myList.messageSumElementsOtherPres(p2);
Finally, to always send messages to a hard coded presence (discouraged: what if you got disconnected?):
//always sends messages to presence 1.
function messageSumElementsFixedPres()
{
//initialize counter
summer = 0;
//walk list and sum.
for s in myList:
summer += s;
//send individual message to all members of mAddrBook
for s in root.p1.mAddrBook.listOfAddressables:
s.sendMessage(str(summer));
}
6) We can declare a handler for presences to handle when presences are destroyed. This handler would, for instance, save all objects' data.
7) For state that should be shared, for instance a global counter, you would create it as follows:
root.counter = 0;
Let's say the messageSumElements function from above also wants to increment this global counter each time it's called, we'd re-define the function as follows:
function messageSumElementsAndIncrement()
{
//initialize counter
summer = 0;
//walk list and sum.
for s in myList:
summer += s;
//send individual message to all members of mAddrBook
for s in mAddrBook.listOfAddressables:
s.sendMessage(str(summer));
//******Change:
root.counter +=1;
//******End change
}
8) When add new presence, we just copy the object tree of the default presence (or have no object tree if no previous presence).
P3 = new Presence(); //copies default presence's object tree.
P4 = new Presence(P2); //copies p2's object tree.
P5 = new Presence(null); //creates a presence with a blank object tree.
Reasons:
*****
-For the case of a single presence, a programmer won't really have to worry too much about the difference between globvars and presence vars. Using either should work, but mixing and matching won't work (because of the implicit default presence).
-For the case of a single presence, mirror keywords won't affect program control.
-When creating multiple presences, state defaults to separated (through copying object tree), so don't have to juggle a bunch of what belongs to whom (recall assumption that there will be more separate data than shared data). Only have to declare what state should be shared (and hang it off of root or hang it off of a single presence, and fix that presence as where the state is stored in all calling functions).
-Because they have similar object trees, presences will behave similarly in different spaces. However, it's easy for the programmer to specifically tinker with presences in different spaces and change behavior.
-Supports copy-and-paste from the internet. Imagine an alternate solution, where programmers needed to specify the context of each object in each function. For instance, returning to the messageSumElements function, a programmer finds the following code snippet on the Internet and copies and pastes it into his/her editor.
function messageSumElementsNoContext()
{
//initialize counter
summer = 0;
//walk list and sum.
for s in root.presence1.myList:
summer += s;
//send individual message to all members of mAddrBook
for s in root.presence1.mAddrBook.listOfAddressables:
s.sendMessage(str(summer));
}
If the programmer wanted this to work for all of his/her presences, he/she would have to make separate copies for each, and remember to over-write all of the times that "root.presence1" is prefixed to words (common way to gather bugs according to CP-miner paper).
-An alternative approach that would also solve this problem is if we had functions pass the "self" keyword. The default presence would be passed as self when the argument isn't specified. Reason not to:
-->May show too much to programmers that just wanted to have a simple, single-presence entity.
-->Consider the messageSumElements function from above with self command. Wouldn't it look like this:
function messageSumElementsSelf(self)
{
//initialize counter
self.summer = 0;
//walk list and sum.
for self.s in self.myList:
self.summer += self.s;
//send individual message to all members of mAddrBook
for self.s in self.mAddrBook.listOfAddressables:
self.sendMessage(str(self.summer));
}
Incidentals:
The structure of a presence:
Field: Addressable.
Field: Geometry (from space).
Field: Presence specific objects.
Issues:
When modifying a function in the code text, how will a programmer know that its a function for presence 1 or presence 2?
--->Mitigated through a good ide, where you can visualize object tree.
What happens when add new presence?
Which direction will the prototype look for things? Can you have a prototype that is in the presence change.
When will we ever be in the root context?
What context will handlers be executed in?