Objects, namespaces, dictionaries, and hashes

DRAFT IN PROGRESS

Prelude

One thing that was apparent whilst writing this proposal up: we're missing something in our declarations…

To create an empty (e.g., 0, 0.0, ””, or FAIL) variable of a given type:

I HAS A <variable> ITZ A <type>

To contrast with and as an adjunct to the usual:

I HAS A <variable> ITZ <value>

BUKKITs

BUKKITs are the container type. They may hold NUMBRs, NUMBARs, TROOFs, YARNs, functions, and other BUKKITS. Each entity within a BUKKIT may be indexed by a NUMBR or a YARN. These indices, whether NUMBRs or YARNs, referring to functions, variables, or other BUKKITs, are generically called “slots”.

Declaration

To create an empty object within the current object's scope:

I HAS A <object> ITZ A BUKKIT

To create an object based upon an existing object:

I HAS A <object> ITZ LIEK A <parent>

Behavior of this sort of inheritance will be described further below.

To define an object, its methods, and the contained variables all at once, the following block may be used within the HAI/KTHXBYE block (or another O HAI block).

O HAI IM <object> [IM LIEK <parent>]
  <code block>
KTHX

One can also use that structure to create an independent object, not immediately bound to the current scope and callable from outside. This is preferred because it encourages code reuse. The same code is used outside the HAI/KTHXBYE block:

O HAI IM <object> [IM LIEK <parent>]
  <code block>
KTHX

More details will follow, but I'm looking to integrate ideas from this thread from xrestaussuredx.

Old commands in the context of objects

The root object, the global scope we have dealt with so far, has been referred to as ”I” in the following language constructs:

I HAS A <variable>
I HAS A <variable> ITZ <value>
HOW DUZ I <function> …?

These forms of the commands effectively create variables and functions within the current object. Since we've been working only in the “root” object, that has always been where the variables and functions have been created. To declare and create variables within an object known in the current scope, I is generalized to an <object> name:

<object> HAS A <variable>
HOW DUZ <object> <function> …?

To add a variable (indexed with a YARN) to an object that has been declared and is within scope:

<object> HAS A <variable> [ITZ <value> | ITZ A <type>]
<object> HAS A <object> ITZ LIEK <parent>

To define a new function within an object currently within scope:

HOW DUZ <object> <function> [YR <param> …]
  <code block>
IF U SAY SO

Slot access

To access slots of BUKKITs (from the scope that contains the BUKKIT), I propose doing so by naming the object and slot, separated by two exclamation points:

<object>!!<variable>
<object>!!<function>
<object>!!<index>

Additionally, runtime access (and array iteration) can be facilitated with some indirection. A question mark is substituted for the second exclamation point, meaning that the identifier that follows is a variable, and the value of the variable is then used as the identifier for the slot:

<object>!?<indirect>

For example:

I HAS A NINDEX ITZ "DISWON"
I HAS A NOBJECT ITZ A BUKKIT
NOBJECT HAS A DISWON ITZ "TREE"
VISIBLE NOBJECT!?NINDEX   BTW "TREE"

The unicode character interrobang (, u203D), and the “exclamation question mark” (, u2049) are both legal substitutes for the pair of characters.

Note: I know this is possible with no punctuation at all. My gut is telling me that we want a bit of distinction here just as markers for the fact that we are pulling in objects and identifiers from a different scope. I'm willing to discuss it, though: this is not an issue that the whole proposal hinges upon.

Numerical slots (indexed with a NUMBR) do not need to be declared before being written to. They do not need to be contiguous: sparse arrays are possible. They may be heterogeneous: they can contain different types.

Inheritance of slots

Declaring a variable within the current object adds that variable to the object.

Accessing a variable from within the current object looks for that variable within the current object. If it is not found, it searches for the variable within the parent object, and on up the chain of parents until it reaches BUKKIT (the base object type), whereupon it fails.

Assigning a variable within the object first searches for it within the current object. If it has been declared within the current object, then it is set. If that fails, it attempts to access it within the parent object. Search continues in up the chain of parents. If the variable name is found up the inheritance chain, then that variable is declared and created within the current object (where the search started), and the value is set. If the variable search fails and the variable was never previously assigned, then it's a declaration error.

In this way, a child object has all of the values and methods of its ancestors, unless it replaces them within itself.

Scope

Functions do not automatically have full access to the containing object's variables. Write access may be gained to only the variables declared with MAH:

MAH <variable> [[AN] <variable> …]

Similarly, objects may declare read/write access to the next object up the containment hierarchy with MAH (but note that this is less useful in general, since with object orientation, we generally know a lot less about the containing object than the inherited type.)

A function has read access to its defined namespace's scope. It gains write access by declaring access with the MAH keyword. It has no (read or write) access to the calling scope.

When writing LOLCODE without objects, with only the primary HAI/KTHXBYE block, functions written in the main block had calling access to each other and to variables defined within that main block (subject to the normal read/write caveats with the MAH declaration). Note that this was because of the definition's scope and not the calling scope.

HAI
  I HAS A LIFE ITZ "GUD"

  HOW DUZ I QUOTE YR THINGY?
    ":":{THINGY}:""
  IF U SAY SO

  HOW DUZ I CHANGE?
    MAH LIFE
    LIFE R QUOTE LIFE
  IF U SAY SO

  VISIBLE LIFE   BTW: GUD
  CHANGE
  VISIBLE LIFE   BTW: "GUD"
KTHXBAI

Note that CHANGE can change a value within current scope, but only because it declared it with MAH. Functions could be read and executed within the current scope normally.

HAI
  I HAS A NUM ITZ 2
  I HAS A TING ITZ A BUKKIT
  TING HAS A NUM ITZ 3
  VISIBLE TING!!NUM            BTW 3
  VISIBLE NUM                  BTW 2

  I HAS A NUTHERTING ITZ LIEK A THING
  VISIBLE NUTHERTING!!NUM      BTW 3
  NUTHERTING!!NUM R 5
  VISIBLE NUTHERTING!!NUM      BTW 5

  TING HAS A BAR ITZ "BAZ"
  VISIBLE NUTHERTING!!BAR      BTW "BAZ"  
KTHXBYE

In this example, both TING and NUTHERTING are contained within the root object. However, NUTHERTING inherits from TING and therefore implicitly starts out with (clones of) TING's values. Since TING's values are already notionally “within” NUTHERTING, there is no need to declare NUM within NUTHERTING before (re-)assigning it. NUTHERTING also gains values that are later added to TING.


(more to come on O HAI and creating/populating a BUKKIT wholesale and in place) — Adam, 2007/07/13 22:20

proposals/1.3/bukkit.txt · Last modified: 2007/07/20 08:13 by atl
Recent changes RSS feed Creative Commons License Donate Driven by DokuWiki