Generics optimization
thekid opened this issue · 1 comments
Scope of Change
Generics will be optimized.
Rationale
- Improve performance
- Enable reflection for generics
- Enable reflective creation of generics
- Support primitives a generic arguments
Functionality
Currently, generics are implemented as follows:
- A: A __generic instance member is required inside the declaration.
It will hold an array of the component types. - B: The methods working with generics are required to take care of
the type checks. - C: The create() core functionality takes care of instantiating the
generics, populating the __generic member with the given types'
names.
Example (abbreviated):
<?php
class Vector extends Object {
public $__generic; // A
public function add($value) {
if (!$value instanceof $this->__generic[0]) { // B
throw new IllegalArgumentException('...');
}
// ...
}
}
$v= create('new Vector<lang.types.String>()'); // C
?>
This has the following downsides:
- The declaration is quite verbose and introduces a bunch of boilerplate
code for the manual component type verification. - At runtime, two generics, e.g. a vector of strings and one integers,
are "instanceof"-compatible - There is no way to type-hint a generic, verifying a vector's component
type is string would mean manually accessing its __generic member.
Plan
A generic instance should be created at runtime named with unique name
created of the base and component types. A class has two names, one that
is reported by XPClass::getName()
(F) and one used literally (L):
<?php
// Creates a class named:
// F: "util.collections.Vector`1[lang.types.String]"
// L: "Vector··String"
$ve= create('new Vector<lang.types.String>()');
// Creates a class named:
// F: "util.collections.HashTable`2[lang.types.String,lang.Generic]"
// L: "HashTable··String¸Generic"
$ht= create('new HashTable<lang.types.String, lang.Generic>()');
// Creates a class named:
// F: "util.collections.Stack`1[string]"
// L: "Stack··þstring"
$st= create('new Stack<string>()');
?>
The same generation process happens for all generic interfaces these
classes implement and the generic base classes they extend.
The middle dot (·, Alt-Gr + "." in Cygwin) is used in the XP Framework
for generated classes and in fully qualified names (see xp-framework/rfc #37). The
cedil sign (¸, Alt-Gr + "´" in Cygwin) is used to separate the components.
Declaration
To declare a generic class using the XP framework, we will resort to
annotations:
<?php
#[@generic(self= 'K, V', interfaces= array('Map' => 'K, V')))]
class HashTable extends Object implements Map {
#[@generic(params= 'K, V')]
public function put($key, $value) { ... }
#[@generic(params= 'K')]
public function get($key) { ... }
#[@generic(return= 'V[]')]
public function values() { ... }
public function toString() { ... }
}
?>
In XP language, this needn't be done as it syntactically supports generics:
<?php
public class HashTable<K, V> implements Map<K, V> {
public void put(K $key, V $value) { ... }
public V get(K $key) { ... }
public V[] values() { ... }
public string toString() { ... }
}
?>
Instantiation
To instantiate generics, the create()
core functionality needs
to be used. The inner workings are as follows:
- Parse string specifying type into class and parameters
- Compose unique name
- If this class exists, instantiate and return
- For all interfaces, perform generation
- Generate class extending lang.Object
- For all methods generate delegation
- Instantiate and return
What we will end up with is the following:
<?php
interface Map··String¸Object {
public function put(String $key, Object $value);
public function get(String $key);
public function values();
}
class HashTable··String¸Object extends Object implements Map··String¸Object {
private $delegate;
public function __construct() {
$this->delegate= new HashTable();
}
public function put(String $key, Object $value) {
$this->delegate->put($key, $value);
}
// ...
}
?>
Reflection
Whether a type is a generic instance should be determinable at runtime:
<?php
// Will display:
// Arguments: [
// 0 => lang.XPClass<lang.types.String>
// ]
$class= create('new Vector<String>()')->getClass();
if ($class->isGeneric()) {
Console::writeLine('Arguments: ', $class->genericArguments());
}
?>
Reflection on generic definitions should also be possible:
<?php
// Will display:
// Components: [
// 0 => "T"
// ]
$class= XPClass::forName('util.collections.Vector');
if ($class->isGenericDefinition()) {
Console::writeLine('Components: ', $class->genericComponents());
}
?>
Also, reflective creation of generic types should be possible:
<?php
$class= XPClass::forName('util.collections.Vector');
$generic= $class->newGenericType(array(XPClass::forName('lang.types.String')));
// ...and then use newInstance() methods
$vector= $generic->newInstance();
?>
Discovering the definition type:
<?php
// Will display:
// Definition: lang.XPClass<util.collections.Vector>
$class= create('new Vector<String>()')->getClass();
if ($class->isGeneric()) {
Console::writeLine('Definition: ', $class->genericDefinition());
}
?>
Type restrictions
The generic types can now be used in parameter type hints, although
somewhat ugly:
<?php
public function dump(List··String $l) { ... }
?>
Security considerations
n/a
Speed impact
Faster.
Dependencies
xp-framework/rfc #197
Related documents
- http://www.artima.com/intv/generics2.html - Generics in C#, Java, and C++
- xp-framework/rfc #106 - original generics RFC
- http://xp-framework.net/rfc/contrib/rfc0193.diff - Implementing patch
(applies in xp5_8 branch only)