Effective BlocksPosted: July 13, 2011
One of the cool features in Gosu is closures, colloquially known as “blocks”. Blocks act kind of like inline function definitions, allowing you to plug bits of logic into other algorithms. (One my standard lines in the talks is “Classes are a tool for generalizing concepts, blocks are a tool for generalizing algorithms.” Pithy and, hey, maybe even true.)
Trying to describe blocks in general terms doesn’t appear to be a great way to explain them: people get lost in the terminology and syntax. The way that I finally “got” blocks wasn’t by staring intently at a text book or wiki page, it was by seeing them in action.
In this vein, I’m going to describe some common programming problems you might face and then show you how blocks can help you solve them elegantly. I hope this will help you understand blocks in Gosu and how use them effectively.
“I’ve got a List of X and I need a List of Y”
This is perhaps the classic problem that blocks can help solve:
- “I’ve got a list of Claims, and I need a list of Claim IDs.”
- “I’ve got a list of Employees, and I need a list of last names.”
To address this problem, Gosu has added the
map() function to
Iterable objects. Taking the two examples above, you can write this code:
var ids = claims.map( \ c -> c.ID ) var lastNames = employees.map( \ emp -> emp.LastName )
The block is the funny little thing starting with a ‘
\‘. Between the ‘
\‘ and the ‘->‘ are the arguments to the block (remember, blocks are kind of like a mini-function) and after the ‘->‘ comes the body of the block.
Taking a look at the first line in the code example, we have a block that takes an argument
c of type
Claim (Gosu infers this type) and that returns the
ID of that claim.
map() function then uses this block to run through all the claims in the
claims list and create a new list, filled with the ID’s of each claim.
Here’s another example:
“I’d like to sort List X by attribute Y”
Some concrete examples might be:
- “I’d like to sort a list of Policies by their effective date.”
- “I’d like to sort a list of Contacts by their age.”
Gosu has an
orderBy() function for this:
var sortedPolicies = policies.orderBy( \ p -> p.EffectiveDate ) var sortedContacts = contacts.orderBy( \ c -> c.Age )
In Java, you might use a Comparator (and then try to remember when to return -1, 0, or 1) in conjunction with
Collections.sort(). I hope you’ll agree that the Gosu above is easier to write and also reads more clearly.
The advantage of the block-based approach becomes even more glaring when you want to add secondary sort columns:
var sortedPolicies = policies.orderBy( \ p -> p.EffectiveDate ) .thenBy( \ p -> p.Status )
Imagine the Java Comparator for that. Ugh.
“I’d like all the elements in List X where Y is true”
This is another classic:
- “I’d like all the Claims that are past due.”
- “I’d like all the Employees that are active.”
Gosu has an
where() function for exactly this:
var pastDueClaims = claims.where( \ c -> c.DueDate < Date.Today ) var activeEmployees = employees.where( \ emp -> emp.Active )
where() function returns a new list where any items that do not meet the criteria expressed in the block are filtered out.
“I’d like to split a List of X up on attribute Y”
This is a more advanced usage of blocks, but it can come in quite handy in some situations:
- “I’d like to split a list of automobiles up based on their color.”
- “I’d like to split my friends up by their birthday month.”
Gosu has a
partition() function for this:
var carsByColor = cars.partition( \ c -> c.Color ) print( "All red cars: " + carsByColor["Red"] ) var friendsByBirthdayMonth = friends.partition( \ f -> f.Birthday.Month )
partition() function splits a list up based on the block you pass in. So, in the first line of code in this example, we split a list of cars up by their color, and get a map, where the key is a color and the value is a list of cars of that color. In the next line we print the list of cars that are red.
You could partition on any attribute: make, model, year, whatever. Blocks help generalize the operation.
Gosu really starts to sing when you compose these methods:
- “I’d like to get the ids of claims past due, ordered by the claim due date”
We can compose three methods together to solve this quite succinctly and clearly:
var pastDueClaimIdsByDueDate = claims.where( \ c -> c.DueDate < Date.Today ) .orderBy( \ c -> c.DueDate ) .map( \ c -> c.ID )
The corresponding java code would be more verbose, and would probably conflate the various operations, making it more difficult to understand what the code was doing.
“Hmm, this is all data structure manipulation…”
The sweet spot for blocks (in Gosu, at least) is in data structure manipulation. There are other areas they can come in handy (callbacks, listeners) but usually you’ll be using them to transform one data structure into another.
Personally, not a day that I’m writing java code goes by that I don’t wish I had blocks. Once you get used to them, it can be very hard to go back. I hope that this quick overview helps you use blocks effectively in your own Gosu code and have a bit more fun when you are coding.