Articles

folding, mapping and applying commands on datastructures and collections

Hi, we have a great update on the collections package featuring methods that allow us to map the list, fold it and to apply commands on every item in the list. This sort of extremely powerful functionality can be compared in intention to using specifications on collections on which we wrote an earlier post here. Some more updates include some bugfixes in the unittests and a stack overflow problem occuring only on macs. But this article will focus on highlighting the high level methods we just added. Want to have a huge boost in productivity and clarity of code??? Read on!

A normal way of manipulating a collection of items (in a list for example) is by using an iterator to loop over the list and in each iteration of the loop we are doing some manipulation on the item. While this is perfectly valid, it is not the best way to do it. Every time we manipulate a list we are potentially duplicating some business logic that we apply to the list and we are duplicating a lot of iteration code.

Also, some of the things we want to do are similar in intention, so if we can abstract the logic we want to apply from the intention we are a step further in writing high level expressive code. In programming, some of these intented steps we want to apply to a list or sequence of values/objects are well know. Here we focus on

  1. Folding, which is an accumulating process on a list, gathering information from each list item to build a return value from the list. This is also know as reduction, accumulation or compression. Examples of this might be getting the sum or the mean of a list of integers. Or a GroupCharacteristic object to use in an artificial intelligence algorithm when going over a list of users. You can come up with your own use cases :)
  2. Mapping, which is the process of transforming one sequence/list of objects into another sequence/list of derived objects. A use case might be to transform a list of domain objects like nl.dpdk.users.User to a generic Object type that can be used to send over the wire to a flash remoting backend. Or to create a list of squared values from a list of integers. Here too, something will probably pop right up in your mind as you are reading this ;)
  3. Applying actions on each list item while not being interested in getting the results in a different list or structure. We are using the Command pattern for this purpose, where we apply a command to each item on the list, thereby potentially transforming the list items. A use case might be updating all items in a list with a permission flag or making all items in a list do some processing on themselves, like having all user interface widgets that we keep reference of draw themselves again on the screen. Here too you could have an ‘aha’ moment.

Let’s demonstrate this in code.

//simple setup with integer values in anonymous objects in the 'value' property of that object.
var list: Ilist = new LinkedList();
list.add({value: 1});
list.add({value: 2});
list.add({value: 3});
 
//ATTENTION DEMO, this is the way we are not going to do it, this is just for the demo of the usual way of doing it.
var iterator: IIterator = list.iterator();
var sum: int = 0;
while(iterator.hasNext()){
	sum += iterator.next().value;
}
trace(sum);//output is 1+2+3='6'
 
//now, this was typical code you see a lot, but which is not very elegant: local variables, iterator plumbing code etc., pay attention to the next piece :)
//END OF DEMO
 
//now apply the different methods on it.
//first, apply a fold on the list, where we want to have the sum of all integer values in the objects.
var folder: IFolder = new SumFolder();
trace( list.fold(folder) );//output is 1+2+3 = '6'
 
//now apply a mapping on the list, where we want to have a new list of integers,
//with the squared values of the original integer values in the anonymous objects in the original list
var mapper: IMapper = new SquaredMapper();
var squared: Ilist = list.map(mapper);
trace(squared.toArray().toString());//outputs '1,4,9' the squared values.
 
//now apply a transformation on the list, where each item is dynamically given a new property
var command: IApplyer = new AddDateCommand();
list.apply(command);
trace(list.get(0).date);//outputs the value of Date.toString() with the time the date property was added.

As you can see, we are using the IFolder, IMapper and ICommandIApplyer interface. Did you see the extremely compact and expressive code fragments that let us do what we wanted to in a very declarative way? Imagine the lines of code this will save you, the amount of typing useless plumbing code: ahhh, the joy!

You can take a look at the interfaces in the source code, but we’ll just demonstrate the previous implementations here. For demo purposes, we left out the constructors.

public class SumFolder extends IFolder{
	//note that we encapsulate business logic in this object. giving it a seperate place in your application instead of scattered through code.
	//initialize, we want to have a valid value for an empty list.
	private var sum: int = 0;
	public function fold(item: *):void{
		//explicitely cast to int
		sum += item.value as int;
	}
 
	public function get():*{
		//return the accumulated value.
		//notice that an IFolder implementation holds state and is passed into a list method
		return sum;
	}
}
 
public class SquaredMapper extends IMapper {
	//note that again, the business logic is encapsulated in a well defined object.
	public function map(item:*):*{
		//just square it and return the value.
		//no casting here... see comments later in the article on type safety
		return item.value * item.value;
	}
}
 
public class AddDateCommand implements IApplyer{
	//wtf? a Command with a parameterized execute method? See comments further down in the article
	public function execute(item: * = null):void{
		//easy, just add the date, note that the business logic is now encapsulated in this object.
		//we implicitely say that item is of type object and dynamic as we are adding the date property
		item.date = new Date();
	}
}

The previous example was very easy of course, but it should give you an idea of the power of these methods. In any serious application, we do not want to have our logic scattered through classes, making us hunt for specific stuff and having us update logic in multiple occurrences. Instead, we want to encapsulate it so we can easily spot it, alter it and have the whole application updated by just changing one class. Also, we do not want to write a lot of lines of plumbing code with for each constructs or iterator loops. Just let the list handle it internally by providing these high level expressive methods.

Of course, you will have to write more classes, but these are small, keep business logic in one place, and adhere to well defined cooperative and reusable interfaces, making your application more readable and maintainable. These design traits are well worth the effort.

Three things deserve a little more attention and are already adressed in the comments of the code above:

  1. The use of Commands
  2. The untyped parameters of the different methods in the interfaces we introduced
  3. Instances of IFolder (and IMapper and ICommand) can hold state.

Regarding the use of Commands, the internal list code for the use of the Command in the list.apply(command) method is

public function apply(command : ICommand) : void {
	var iterator : IIterator = iterator();
	while(iterator.hasNext()) {
		command.execute(iterator.next());
	}
}

We discourage the use of using a parameterized execute() method on an ICommand. Nevertheless, we refactored the ICommand interface to do just that. By default the argument is null and this was really the only use case where we felt that the use of the parameterized execute method was justified. We refactored this back to an argumentless execute() method.

Normally, any, any context that is needed in a concrete Command class is passed at construction time, and can be queried at execution time to get the current application’s values it needs. The need to supply context in the execute method creates a dependency from the Command to it’s context which goes against the principle of loose coupling and against one of the main use cases of Commands which is that they can be passed around anonymously without knowing their execution context.

Let me state again, context passed in the execute method of a Command is wrong. In this case however, the functionality was needed for the list.apply() method and was so command like, that we added it to the ICommand interface, instead of creating a seperate interface only for that purpose. Therfore, the non use of a parameter is default on the ICommand.execute() method. We refactored this, use an implementation of IApplyer instead (it has an execute() method with argument)

Regarding the untyped parameters of the different methods in the interfaces and on the list itself, this is ofcourse necessary as flash actionscript 3 has no generic type like in Java generics on their collections. We need the lists to be generic and accept any kind of data, but we lose type safety in the process. Therefore, casts will be needed outside of the collection classes, and inside the Imapper, IFolder, ICommand and ISpecification implementations to make fully use of the inhabitants of the collection / list structures. Therefore, while this is not a design fault of our code, but rather a consequence of the way actionscript works, we strongly advise to take extra care in coding correctly and having your code explicitely cast the data to the right datatype. This is especially important when other programmers in the team are working with your code.

Regarding the use of state in the interface implementations of IFolder, IMapper and ICommand, this is very convenient as for example a call to sum = list.fold(folder); returns the sum, but the original folder instance can hold other information about the whole operation, that can directly be queried after the previous piece of code. In the previous example, it would also have been possible to calculate the mean, the average, the standard deviation etc. This means that IFolder instances can also be very powerful object instances that solely implement the interface, but can hold a lot of other functionality too.

The flipside is that especially for IFolder implementations you have to be careful not to reuse them, or reinitialize/reset them before making a second call, because they aggregate data which is kept in the instance.

To wrap it up: in nl.dpdk.collections.core and nl.dpdk.commands, the different interfaces used here can be found, and furthermore, the interfaces IApplyable, IFoldable, IMappable and ISelectable (for specifications) can be used in your own applications, in subclasses of the framework provided here, or in your own custom classes.

Happy coding!

3 Responses to “folding, mapping and applying commands on datastructures and collections”


  1. 1 Johnnie Estrem

    This is awesome , I’m sure that technology will get much much better.

  2. 2 Dhyana Sarkar

    Heard about this website from my friend. He pointed me here and told me I’d discover what I need. He was correct! I acquired all of the concerns I had, answered. Did not even get lengthy to seek out it. Love the fact that you produced it so simple for individuals like me.

  3. 3 Der Bohrhammer

    I’ll right away snatch your rss feed as I can not in finding your e-mail subscription hyperlink or e-newsletter service. Do you have any? Kindly let me recognize in order that I may subscribe. Thanks.

  1. 1 Factory for creating ResultSet from an SQLResult in AIR at dpdk Open Source

Leave a Reply