thekid/rfc — Issue #106: RFC 0106: Array access / iteration / type boxing / generics
Author: thekid
Created: 2011-10-09 15:08:05+00:00
State: open

Scope of Change
====
The lang.types.ArrayList class and the relevant classes from util.collections
will be changed to support array access (read/write) and foreach() iteration.
The classes from the collections API will furthermore support type boxing
and provide generic variants of themselves. The text.String class will be
deprecated and replaced by lang.types.String and lang.types.Character.

Rationale
====
Easier / intuitive interface to "array" types.

Functionality
====

Class overview
----

```
* lang.types.ArrayList is an immutable, zero-indexed list of values of any
type and most closely resembles an array in Java.

* util.collections.Vector is a resizable array of objects.

* util.collections.HashTable is a map where both keys and values are
objects.

```

Array access
----
The ArrayAccess interface requires implementing the following methods:
offsetGet(), offsetSet(), offsetExists() and offsetUnset(). These
overload getting ($value= $object[$key]), setting ($object[$key]= $value)),
testing (isset($object[$key])) and removing (unset($object[$key])).

```php
add(new String('hello'));
$v[]= new String('hello');

// The get() method and its overloaded equivalent
$hello= $v->get(0);
$hello= $v[0];

// The set() method and its overloaded equivalent
$v->set(0, new String('world'));
$v[0]= new String('world');

// The remove() method and its overloaded equivalent
$v->remove(0);
unset($v[0]);
}

with ($h= new HashTable()); {

// The put() method and its overloaded equivalent
$h->put(new String('stage-url'), new URL('xp://localhost:6448'));
$h[new String('stage-url')]= new URL('xp://localhost:6448');

// Primitives in keys are "boxed"
$h->put('stage-url', new URL('xp://localhost:6448'));
$h['stage-url']= new URL('xp://localhost:6448');

// The get() method and its overloaded equivalent
$url= $h->get(new String('stage-url'));
$url= $h[new String('stage-url')];

// Primitives in keys are "boxed"
$url= $h->get('stage-url');
$url= $h['stage-url'];
}

with ($s= new HashSet()); {

// The add() method and its overloaded equivalent
$s->add(new String('X'));
$s[]= new String('X');

// The contains() method and its overloaded equivalent
$s->contains(new String('X'));
isset($s[new String('X')]);

// The remove() method and its overloaded equivalent
$s->remove(new String('X'));
unset($s[new String('X')]);
}
?>
```

Iteration
----
The IteratorAggregate interface requires implementing a method called
getIterator() which must return an Iterator instance. This makes a class
usable inside the foreach statement.

```php
addAll(array(new String('1'), new String('2');
foreach ($s as $value) {
echo $value, ' ';
}

// HashTable foreach-iteration, prints "development production "
$h= new HashTable();
$h['development']= new URL('xp://localhost:6448');
$h['production']= new URL('xp://jboss.example.com:6448');
foreach ($h->keys() as $key) {
echo $key, ' ';
}
?>
```

Generics
----
To be able to create generics with the "new" keyword, we would need to
extend PHP's syntax or change constructor signatures. Both of those have
obvious downsides.

Therefore, a new core function "create" is introduced. Here's its prototype:

```
lang.Generic create(mixed arg) throws IllegalArgumentException

```
To create a generic object, the argument passed to create() must be a string
of the form ClassType "<" ComponentType [ ", " ComponentType ] ">". Class
names referenced by ClassType and ComponentType may be qualified or not and
need to refer to loaded XP classes or interfaces.

Examples:

```php
')); {
$hash['key']= new String('value');
$hash['key']= new Integer(1); // Throws an IllegalArgumentException
$hash[1]= new String('value'); // - " -

$value= $hash['key'];
$value= $hash[1]; // Throws an IllegalArgumentException
}

with ($func= create('util.collections.HashTable')); {
$func['hello']= newinstance('lang.Runnable', array(), '{
public function run() {
Console::writeLine("Hello");
}
}');
$func['hello']->run();
}

with ($vector= create('Vector')); {
$vector[]= new Integer(1);
$vector[]= new Float(1.0);
$vector[]= new Long(6100);
}

with ($routines= create('HashSet')); {
$routines[]= XPClass::forName('lang.reflect.Method');
$routines[]= XPClass::forName('lang.reflect.Constructor');
}
?>
```

Supporting generics in classes
----
To add generics support to a class, a public member named "__generic" must
be introduced. In all methods, the generic types must be verified manually.

Example:

```php
__generic && !$element instanceof $this->__generic[0]) {
throw new IllegalArgumentException(
'Element '.xp::stringOf($element).' must be of '.$this->__generic[0]
);
}

// ...
}
}
?>
```

This poses a higher burden when programming (compared to one being able to
express this syntactically) but does not introduce a great performance
penalty like http://experiments.xp-framework.net/?people,friebe,generics
does (transforms syntactically defined generic declarations to the notation
seen above when a class is loaded).

Additional note: create() overloaded
----
The create() functionality provides an overloaded version of itself:
When passed a lang.Generic instance, the instance is simply returned.

This serves the purpose of chaining after "new", which is not supported
by the PHP language:

```php
toString(); // Parse error
?>
```

The following is the workaround:

```php
toString();
?>
```

Additional note: ArrayList vs. Vector
----
The distinction between those two is that ArrayList is a thin, immutable
wrapper type without utility functions such as indexOf() or contains(),
whereas the Vector class provides a full "list" API.

Additional note: ArrayList constructor
----
lang.types.ArrayList's constructor will be changed to accept varargs
instead of an array. The classes that use lang.types.ArrayList will
be adapted to this change.

To be able to create an ArrayList from an array or from a known size,
a new method "newInstance" will be added. Usage as follows:

```php
length;

// Constructing an empty arraylist
$a= ArrayList::newInstance(4);
$four= $a->length;
?>
```

Security considerations
====
n/a

Speed impact
====
Slightly slower (more methods, extra implementation checks)

Dependencies
====
- A new interface util.collections.IList will be introduced. An
implementation will exists in form of the new class
util.collections.Vector.

- xp-framework/rfc #101 - The lang.types.String class will be needed for
boxing to work.

Related documents
====
- http://www.php.net/~helly/php/ext/spl/interfaceArrayAccess.html
Zend-Engine internal interface ArrayAccess

- http://www.php.net/~helly/php/ext/spl/interfaceIteratorAggregate.html
Zend-Engine internal interface IteratorAggregate

- http://xp-framework.net/rfc/contrib/rfc0106-remote.diff
Patch for remote API (apply in xp/trunk)

- http://experiments.xp-framework.net/?arena,arrayaccess
Experiment and unittests

- http://java.sun.com/docs/books/tutorial/java/generics/
Generics lesson

- http://msdn2.microsoft.com/en-us/library/ms379564(VS.80).aspx
An Introduction to C# Generics

Comments
====
- friebe, Mon Jan 8 19:09:55 2007
The ArrayAccess and IteratorAggregate interfaces are part of the Zend
Engine, not of SPL!

- friebe, Tue Apr 24 18:06:11 2007
Maybe we can add declarative generic syntax as seen in this experiment:
http://experiments.xp-framework.net/?people,friebe,generics later on;
it can then be decided whether to live with the performance penalty
or to if a compilation step should be introduced (which would be a
step into the direction of xp-framework/rfc #52 and the XP language, see also
http://experiments.xp-framework.net/?people,friebe,jay)

- friebe, Tue Apr 24 19:30:20 2007
http://www.php-compiler.net/doku.php?id=core%3aphalanger_for_.net_developers
Section 3.3: PHP/CLR language extensions notes Phalanger uses:
ClassType "<:" ComponentType [ "," ComponentType ] ":>"
for generic syntax (note the extra ":"), for example:
$d= new Dictionary<:string, string:>();