overview
summary of proposed ECMAScript 4.0 features not already in ActionScript 3.0
language still a work in progress (this lecture last updated april 17 2008)
ECMAScript 4 specification release date: December 2008
resources...
structural types
record type
array type
record type
record type: describes the structure of a set of objects
type Point = { x: int, y: int }
any object with fixed int x and int y belongs to Point
x and y, but may have other members)record type examples
new record type instance, with args for x and y
new Point( 3, 10 )
new record type instance, type set with type annotation
{ x:3, y:10 } : Point
inline usage:
public function moveTo (p:{ x: int, y: int }) { x = p.x; y = p.y; } moveTo(somePoint); moveTo(someSprite); // Failure because untyped object doesn't have required type. moveTo({x:10, y:20}); // Success because object's type is specified. moveTo({x:10, y:20}:{ x: int, y: int });
array type
array type: type that describes the structure of an array
type intArray = [int]; // All elements must be int
type myArray = [int,string]; // First element must be int, // second must be string, // remaining elements unconstrained
new array type instance, with 7 elements:
new intArray(7)
new array type instance, type set with type annotation
[ 1,2,3 ] : intArray
nested structural types
type Person = { name:{ last:string, first:string }, born:Date, spouse:* }
[[int]] // Array of arrays whose elements are type int
union types
// x can be assigned either an int or a string var x:(int|string)
public function send (msg:(string|XML)):void {...}
Boolean, String, Number replace ES3 primitives
CURRENT BUILT-IN STATUS IS IN FLUX
ECMAScript 4 built-ins:
wrapper classes for boolean, string, double:
wrappers are dynamic, non-final, nullable
wrappers behave like ES3's primitives of the same name
predefined union types
new union types:
type AnyString = (string,String) type AnyBoolean = (boolean,Boolean) type AnyNumber = (byte,int,uint,double,decimal,Number) type FloatNumber = (double,decimal)
handy for writing libraries used by legacy and new code
type constraints on rest arguments
"rest" arguments can have type constraint
function drawPolygon (p1:Point, p2:Point, p3:Point, ...[Point]): void
non-nullable types
// "!" indicates that v cannot be assigned null var v : C!
// "?" indicates that v can be assigned null var v : C?
// vars of type C cannot be assigned null class C! { ... }
rationale: earlier (compile-time) detection of null-pointer errors
instance initialization
assign constructor parameter directly to an instance variable
// Assign x the value of parameter p class C { var x : D! // must be initialized function C (p):x = p { ... } }
the like operator
like tests whether an object is structurally similar to a given type
// v will accept any of these: // { x:3, y:3 } : Point // { x:3, y:3 } // new MovieClip() var v: like { x: int, y: int }
use case: old ECMAScript 3 code using upgraded ECMAScript 4 library
the wrap operator
THIS FEATURE HAS BEEN RETIRED
wrap can give an untyped object a type
o has no type:
var o:* = { x: 10, y: 20 };
give o type Point:
type Point { x: int, y: int }; var v: wrap Point = o;
legally assign o (now with type) to w (whose type is Point):
var w:Point = v;
parameterized types
datatype used in class code dynamically determined on a per-instance basis
similar to C#'s "generics"
used primarily to implement core-language-level collections
class Hashtable.<K,V> { var store : [ [K,V] ]; function fetch(k:K) : V { var ix : uint = hash_func(k); while (store[ix][0] != k) ix = ... // some probe sequence return store[ix][1]; } } // Make a new Hashtable, with uint keys and string vals var h : Hashtable.<uint,string> = new Hashtable.<uint,string>();
lexical block scoping
use 'let' instead of 'var' to create block-scoped vars
{ let a = 10, b=1 { let a = 20 trace(a+b) // 21 } trace(a) // 10 }
expression closures
single-line functions
function getWidth () this.width;
&&= and ||=
&&= not often used
||= used for assigning default values
// this code... myVar = myVar || "default"; // can be shortened to: myVar ||= "default";
generic functions ("multimethods")
THIS FEATURE HAS BEEN DEFERRED
java-style method overloading
create generic function first:
generic function intersect(s1, s2);
attach ("overloaded") methods to the generic function:
generic function intersect(s1: Shape, s2: Shape) { // general intersection method } generic function intersect(s1: Rect, s2: Rect) { // fast intersection for rectangles }
not currently available in instance-method form
program units
THIS FEATURE HAS BEEN DEFERRED
similar to #include, but based on semantics, not files
unit URLParser { use unit Splitter "http://www.mycompany.com/lib/splitter" use unit Unicode "http://www.mycompany.com/lib/Unicode" package com.mycompany.urlparser { ... } } use unit URLParser "http://www.mycompany.com/lib/URLParser"
iterators
based on python's iteration protocol
an interable object must have the following structural type:
type IterableType.<T> = { iterator::get: function (boolean=) : IteratorType.<T> };
an iterator object must have the following structural type:
type IteratorType.<T> = { next: function () : T };
for (i in o) ... updated to perform iteration equivalent to:
let ($it = o.iterator::get(true)) { while (true) { try { i = $it.next(); } catch (e : iterator::StopIterationClass) { break; } ... } }
example showing iteration over Fibonacci sequence:
// The iterable and iterator class: class Fibber { private var a=0, b=1 private var cutoff:int = int.MAX_VALUE; public Fibber (cutoff:int) { this.cutoff = cutoff; } iterator function get(deep:boolean = false): IteratorType.<int> this public function next(): int { if (b >= cutoff) throw iterator::StopIteration [a,b] = [b,a+b] return a } } // Usage: for ( i in new Fibber(1000) ) trace(i)
generators
generator: function that generates a sequence of values for iteration
when called, the generator returns an iterator, but does not run
calling iterator.next() does 3 things:
yield// A generator that yields 3 values function countdown() { yield 3; yield 2; yield 1; } var counter = countdown(); trace(counter.next()); // 3 trace(counter.next()); // 2 trace(counter.next()); // 1 // None left, so iterator::StopIteration exception is thrown trace(counter.next()); // ERROR!
python inspired
generators: rationale
rationale: elegant way to traverse data structures (esp. trees)
// A generator for finding leaves in a tree structure function fringe (tree) { if (tree is like {left:*, right:*}) { for (let leaf in fringe(tree.left)) yield leaf for (let leaf in fringe(tree.right)) yield leaf } else { yield tree } }
// Make a sample tree var tree = { left: { left: 37, right: 42 }, right: "foo" }
// Iterate over the tree for ( let x in fringe(tree) ) { trace(x); // 37, 42, "foo" }
operator overloading
redefine the behaviour of built-in operators for custom types
cannot redefine operations for built-in types
generic intrinsic function + ( list: UserList, user: User ) { list.push(user); return user; } // Usage: userList + user;
the call stack (proper tail calls intro)
PROPER TAIL CALLS HAVE BEEN DEFERRED
"call stack": list of the functions currently executing
each function call adds a new item ("stack frame") to the stack
a stack frame indicates where to resume execution after its function exits
when a function exits, its stack frame is removed
function submitForm () { validateInput(); sendInputToServer(); } submitForm();
##[diagram] call stack: =========================== Frame: submitForm() =========================== Frame: validateInput() Frame: submitForm() =========================== Frame: sendInputToServer() Frame: submitForm() =========================== Frame: submitForm() ===========================
proper tail calls
THIS FEATURE HAS BEEN DEFERRED
tail call: function calls another function as its last operation
function getArea (w, h) { return multiply(w, h); }
"proper tail call": run the tail-called function without adding to the call stack
i.e., return multiply()'s result directly to getArea()'s caller
prevents stack overflow (out of space) for recursive functions
enables popular functional programming idioms: state machines, recursion
meta-level hooks
override meta::invoke to intercept method calls
other overrides:
meta function set(name, value) { trace("Setting " + name + " to " + value); }
used internally to handle operations for built-in types
reformed with
THIS FEATURE HAS BEEN DEFERRED
specify which properties should be found in the object
var p = new Point(1, 2); p.x = 1; p.y = 2; var x = 10; var y = 20; // Only find x in p with (p):{x: int} { trace(x + y) // 21 }
more numeric types
THIS FEATURE IS IN FLUX; LIKELY REMOVAL
ActionScript 3.0: int, uint, Number
ECMAScript 4: int, uint, byte, double (64-bit, like old Number), decimal (128-bit, 34 digits)
decimal context
THIS FEATURE HAS BEEN REMOVED
set rounding and precision options for a block of code
// 12 digits of precision, round to even ctx = new DecimalContext(12, "half_even") { use decimal ctx // "+" computed to 12 digits of precision, // rounding to even if necessary x = a + b }
'this' preservation
this preserved for nested function calls
no need to store 'this' in a local variable
function f () { function g() { trace(this.x) } g(); // returns 'x' for f()'s 'this' } var v = { x: 10, f: f } v.f() // Displays: 10
cast operator
ActionScript 3.0: Apple(food)
ECMAScript 4: food cast Apple
'this function'
ActionScript 3.0: arguments.callee
ECMAScript 4: this function
'switch type'
switch type (v) { case (x: XML) { ... } case (s: String) { ... } // The "default" case case (o:*) { ... } }
cases don't "fall through"
array comprehensions
python inspired
convenience syntax for array initialization
assign values from an iterator to an array's elements
[expression loop condition]
function range(begin, end) { for (let i = begin; i < end; ++i) { yield i; } } var tenSquares = [i*i for (i in range(0, 10))]; var tenEvenSquares = [i*i for (i in range(0, 10)) if (i%2 == 0)];
// Create an array of Fibonacci numbers up to 1000 var fibTo1000 = [i for (i in new Fibber(1000))]
destructuring assignment
shortcut syntax for assignment
assign values from an array/object to a set of variables
e.g., assign variable values based on array elements:
var a; var b; // Assign 1 to a, and 2 to b [a, b] = [1, 2];
e.g., return multiple values from a function:
function getDetails():Array { return ["Colin", "Toronto"]; } // Assign "Colin" to name and "Toronto" to city [name, city] = getDetails();
e.g., assign variable values based on an object's variables:
var a; var b; var o = {name:"Colin", city:"Toronto", occupation:"Author"}; // Assign "Colin" to a and "Toronto" to b {name:a, city:b} = o;
numeric suffixes
THIS FEATURE IS IN FLUX
Suffixes on numeric literals denote their type:
-7i, 1u, 37d, 14.5m
i = int, u = uint, d = double, m = decimal
triple-quoted strings
THIS FEATURE HAS BEEN DEFERRED
can contain line breaks and unescaped quote characters
"""this string starts here it has a "blank" line before this one and ends here:"""
string indexing
// ActionScript 3.0, yields "e" "hello".charAt(1);
// ECMAScript 4.0, yields "e" "hello"[1]
slicing syntax
// ActionScript 3.0, yields "el" "hello".slice(1, 3);
// ECMAScript 4.0, yields "el" "hello"[1:3]
Vector class
high-performance, monotyped array
offers: fixed length (optional), no gaps, bounds checking
Map class
standard hash table
new global function: intrinsic::hashcode
item inspection/insertion/removal methods:
size():uint; get(key: K):V?; put(key:K, value:V):void; has(key:K):boolean; remove(key:K):boolean;
item iteration
get(deep:boolean = false):iterator::IteratorType.<K>; getKeys(deep:boolean = false):iterator::IteratorType.<K>; getValues(deep:boolean = false):iterator::IteratorType.<V> getItems(deep:boolean = false):iterator::IteratorType.<[K,V]>
reflection
typeOf(value) returns a "meta-object" that describes value
example meta-object APIs:
interface ClassType extends NominalType { function construct(typeArgs: TypeIterator, valArgs: ValueIterator): Object; }
interface NominalType extends Type { function name(): String; function namespace(): String; function superTypes(): IteratorType.<ClassType>; function publicMembers(): FieldIterator; function publicStaticMembers(): FieldIterator; }
interface Type { function canConvertTo(t:Type): Boolean; function isSubtypeOf(t:Type): Boolean; }
check if a value's type inherits from T:
typeOf(value).isSubtypeOf(T)
make a new instance of oldValue's type:
var newValue = typeOf(oldValue).construct();
fixed variables on generic objects
use var to make a non-deleteable instance variable on a generic object:
var order = {var quantity:1, var price:5.99, comment:"thanks!"); delete order.quantity // error in strict mode, otherwise no effect delete order.price // error in strict mode, otherwise no effect delete order.comment // ok, deletes the variable 'comment'
make all instance variables non-deleteable:
var order = var{quantity:1, price:5.99, comment:"thanks!");
const instead of var makes the variable read-only and non-deleteable
adds structural integrity to generic objects
should improve performance of generic objects