This month I’ve decided to open-source a new collection of ActionScript 3 utilities that I’ve piecing together over the past couple weeks. These utilities are all aimed at using higher-order functions in order to improve AS3 code semantics and condense expressions. Of course, many of these utilities are inspired from other functional languages like Haskell, Scala, and some parts of CoffeeScript. If you’re wondering, why do we need functional style utilities at all?

Why use FP in Flash [or any imperative language]

These two articles will help cover the basics: Why Functional Programming Matters Matters and What’s wrong with the for loop? The gist, in terms of list comprehensions, is that for loops are a major headache with imperative languages as they muddle the concerns of what you are doing with a list of data. In computer science, almost all concerns with collections boils down to three notions: filtering, reduction and transformation. Filtering is making an array of just the elements that you want to isolate. Reduction is looking over every element and deriving an accumulated value over it- for example, taking the sum of every number in a list to be used as a final result. Transformation, also called mapping, converts or manipulates the data in a certain [ideally, deterministic] way. By strictly isolating these concerns, your code becomes much more readable and flexible.

String Lambdas

This feature is composed of two global methods lambdaMap and lambdaFilter. This is implemented as a first step towards creating an AS3 version of the fantastic Katy library for CoffeeScript. There is an initial implementation of combinators in the library, but it needs some polish before I show examples. The basic idea of string lambdas is to get around the fact that AS3 does not have a compressed form of lambdas. For example, in some FP languages, you can quickly write

x->x % 2==0

to make a function that takes in a value and returns a Boolean if it can be divisible by 2. However, in AS3, you would have to write this lengthy monster:

function(x:int):Boolean { return x % 2==0; }

String lambdas are neat time savers as they use symbolic Strings to create these commonly needed functions for you.

private function lambdaTest():void {
var data:Array = [1,2,3,4,5];
var data1:Array = lambdaMap(data, "+", 3);
// The result is: 4,5,6,7,8
trace(data1);
data1 = lambdaMap(data, "*", 3);
//lambdaMap *3: 3,6,9,12,15
trace("lambdaMap *3:", data1);

data1 = lambdaMap(data, "==", 3);
//lambdaMap ==3: false,false,true,false,false
trace("lambdaMap ==3:", data1);

data1 = lambdaMap([[1,2,3],[1,2]], "+", [4,5]);
//lambdaMap [[1,2,3],[1,2]] +[4,5]: [[1,2,3,4,5], [1,2,4,5]]
trace("lambdaMap [[1,2,3],[1,2]] +[4,5]:", "[["+data1[0]+"], ["+ data1[1]+"]");

data1 = lambdaFilter(data, "!=", 3);
//lambdaFilter !=3: 1,2,4,5
trace("lambdaFilter !=3:", data1);

data1 = lambdaFilter(data, ">", 3);
//lambdaFilter > 3: 4,5
trace("lambdaFilter > 3:", data1);

data1 = lambdaFilter(data, "<=", 3);
//lambdaFilter <= 3: 1,2,3
trace("lambdaFilter <= 3:", data1);
}

 

XML Comprehensions

While it’s fairly simple to iterate over XML lists using FOR EACH, I wanted to design something that directly expressed the transformation of the list into another data type. Since XMLList is not polymorphic with Arrays, I’ve created xmlMap utility to loop over each element of a child XML node, give that XML chunk to a functional object that returns some value, and builds an array of that returned value.

private function xmlParsing():void {
var raw:String = "StarcraftDiabloWarcraft";
var data:XML = new XML(raw);
var result:Array = xmlMap(data.game, mapXMLData);
//xmlMap: Game:Starcraft, Game:Diablo, Game:Warcraft
trace("xmlMap:", result);

// create a TextField for each node and add them to the display
xmlMap(data.game, mapXMLSprites).forEach(listCall(addChild));
}

// Used to transform an XML element into a constructed String with a prefix
private function mapXMLData(node:XML, index:int, xml:XMLList):String {
return " Game:" + node.toString();
}

// Used to transform an XML element into a TextField
private function mapXMLSprites(node:XML, index:int, xml:XMLList):TextField {
var field:TextField = new TextField();
field.text = " Game:" + node.toString();
field.y += index * field.textHeight;
return field;
}

The first example above transforms an XMLList into a Array of Strings. Admittedly, while this isn’t very practical, the second example shows the practical use case.

xmlMap(data.game, mapXMLSprites).forEach(listCall(addChild));

The above line takes the XMLList from data.game and calls mapXMLSprites for each child XML element. The function mapXMLSprites then creates a TextField for that element and returns it. The utility then adds that return value to an Array. Once this is all done, the expression “forEach(listCall(addChild))” maps over the returned Array from the utility (forEach is an existing method on Array) to call addChild. Notice we make use of another utility “listCall” which removes part of the function object required definition. Without listCall, you would have the write the code as below:

function addChildByList(view:TextField, index:int, list:Array):void {
addChild(view);
}
xmlMap(data.game, mapXMLSprites).forEach(addChildByList);

The ActionScript Array methods map and forEach require the functional object signature of (element:ArrayItemType, index:int, list:Array). The utility listCall transforms this requirement to just (element:ArrayItemType). This bridges the functional interface to call simple value methods like addChild without having to manually create a function wrapper around it. Behind the scenes, listCall is creating the proper function definition for forEach and then calling your provided method with just the list element as a parameter.

More Updates Coming

There are several additional utilities in the repository [function guards, combinators, cons, zipl, zipr], but most of the others require more love before I’m willing to blog about their implementation.

This library is very much a work in progress as I experiment with how to write more powerful expressions in AS3. Eventually, I’ll begin to port some of these utilities over to haXe where I can make use of inlining and meta scripting to improve performance and syntax. The one caveat with these functional utilities is that they do perform poorly and take a small amount of additional memory, but this is mostly due to the limitations of the language and run-time. However, this shouldn’t be a concern if you’re simply using them when constructing stuff versus using them on a frame by frame basis.

AS3FP @ GITHUB: https://github.com/jadbox/AS3FP
Other news: IsoHill has been updated to work with Starling 1.0 release.


Author: Jonathan Dunlap
Jonathan is a veteran software architect, author of IsoHill, humanitarian, and has worked with Bigpoint Inc, CrowdStar, ePrize, and Microsoft.
Be Sociable, Share!