For Loops For Erlang Beginners

Newcomers to Erlang are often dismayed to learn that it does not provide syntax for any of the looping constructs typical to procedural languages. This means no while, no for, and no foreach*.

In this article I’ll show you how to write a function in Erlang that emulates the functionality of the for loop that is familiar to most programmers. Using it, you will be able to have an arbitrary code path be executed a specified number of times.

What We’re Up Against

In a procedural language like C we can write something like this

for(int i = 0; i < 10; i++)
{
  cout << "Line " << i << endl;
}

Since this tutorial is aimed at beginners, I’ll go over the basics of Erlang functions before diving into how to emulate the above code.

Functions, Pattern Matching, and Guard Sequences

In Erlang, you may specify multiple patterns against which calls to a function will be matched. This is similar to function overloading in procedural languages. One important thing to note is that Erlang functions are identified by the combination of their name and their arity. Arity refers to the number of parameters that a function accepts. If we define two functions with the same name but different arity, Erlang will consider them to be two separate functions. Here’s an example of a function of arity 1 that has four different patterns.

example(N) when is_list(N) ->
    io:fwrite("A list! ~n")
;
example(N) when N < 5 ->
    io:fwrite("An integer less than 5!")
;
example(N) when N > 5 ->
    io:fwrite("An integer less than 5!")
;
example({A, B, C}) when is_integer(A) ->
    io:fwrite("An tuple with three elements, the first of which is an integer!")
.

When calling a function, the Erlang runtime will attempt to match how the function was called with one of the available patterns. It does this comparison sequentially in the order that the patterns are defined. Two things make up the pattern: the structure of the arguments themselves and the guard sequence. The first three versions use guard sequences to define their pattern while the last example uses both a guard and the structure of the argument itself. Here are four calls that will match the patterns of the above function

%Passing a list
example([1, 2, 3, 4, 5]).
%Passing an integer less than 5 and greater than 5
example(3).
example(6).
%Passing a tuple with three elements
example({1, hello, world}).

Any of the following calls would cause the runtime to throw an error since the calls wouldn’t match any of the patterns specified for the function.

%An integer equal to 5
example(5)
%A tuple with four elements
example({1, 2, 3, 4})
%A tuple with three elements whose first element is not an integer
example({hi, there, world})

Writing The For Function

Here’s how we envision a user calling our code to duplicate the C example from above.

util:for(10, fun(N)-> io:fwrite("Line ~B~n", [N]) end).
util:for(10, fun myModule:printLine/1).

The first line uses Erlang’s anonymous function syntax to specify the code that will be the body of the for loop. The second line shows how to use the fun [module]:[function]/[arity] syntax to pass in a function that has been defined elsewhere.

Here’s how we implement util:for

module.erl

-module(util).

%external functions
-export([for/2]).

%
% for-loop emulation
%
% N:     Number of times Fun should be called
% Fun: The high-level function to be called.
%          This function is expected to accept a single integer (the loop count)
%
for(N, Fun) when is_integer(N), is_function(Fun, 1) ->
    util:for({N, 0}, Fun)
;
for({N, _}, _) when is_integer(N), N < 1 ->
    ok
;
for({N, LoopCount}, Fun) when is_integer(N), is_function(Fun, 1) ->
    Fun(LoopCount),
    util:for({N-1, LoopCount+1}, Fun)
.

First we name our file util.erl since we’ll be placing our function in a module called util. In Erlang, every file defines a module and the name of the file and the module must agree.

Next we declare that we are exporting a function named for of arity 2. The -export keyword makes the function externally visible so that it can be called from other modules. If we did not export the function, it could only be used from within the util module.

Finally, we actually implement the function.

Our function accepts two arguments: an integer and a function that accepts one parameter. These requirements are enforced using guard sequences. For a full list of valid guard sequences, check out section 6.24 of the Erlang Reference Manual.

When the first, “base” pattern of the function is matched, it hands off control to one of the other two patterns.

The second pattern makes use of the _ expression. This is like a wild card. In this pattern, we don’t care about the loop count or the function. This is our “backstop” case that terminates the recursion and it is matched when N is less than 1. It’s important that we define this pattern before the one below it, since they both have the same argument pattern. Remember that Erlang tries to match the patterns sequentially. If the third pattern came before the second one, it would always match and our recursion would never end.

The third pattern calls Fun and passes it the loop count. It then recursively calls itself, decrementing the number of times the function should be called and incrementing the loop count. After reading the last sentence, observant readers might wonder about the fate of the stack. What happens if we call our function and specify an incredibly large number of iterations? Won’t we eventually eat up all the available memory and overflow the stack? Luckily for us, the answer is no! The Erlang compiler is very smart and will be able to optimize our function since it is tail recursive. Tail recursion is when the last instruction of a recursive function is the recursive call. In situations like this, the compiler recognizes that nothing on the stack frame will ever be needed again and so it frees that memory for garbage collection.

That’s all there is to it!

Just Kidding, There’s More

Not bad for a first shot, but the C example is still more powerful since it can be configured with different start, end, and step criteria. In a follow up article I’ll show you how to expand util:for to support these features. But why wait? Everything you need to know has already been revealed!

Until next time, happy coding!

* Erlang does contain a foreach-like function in the lists module, but it’s only for iterating over a list of items and applying a function to each one.

Of Blocks and Returns

In Ruby, blocks are anonymous functions that you can pass to other functions that have been written to accept them. When passed to such a function, that function can use the yield statement to pass variables and defer execution to the block.

An example of a function that accepts blocks is the Array class’ reject!() method. reject!() iterates over the array’s elements and evaluates the given block once for each element. If the block returns true, the element is removed from the array.

The term “returns” is tricky, though. In Ruby, all functions automatically return the result of their last evaluated statement.

A piece of code like this will work just as expected, removing all elements of the array greater than five.

myArray.reject! do |element| 

	element > 5

end

“element > 5″ is a boolean statement and since it is the last statement of the block, its value will also be the return value of the block.

Let’s try something a little more complex, though.

We will declare a Test structure that has a name and an attempt count. The idea will be to create an Array of three Tests with different attempt counts and loop over it three times, each time calling reject!() on the Array. We will pass a block to reject!() that will either return true if the attempt count of a Test exceeds three or increment the attempt count and return false otherwise.

Test = Struct.new(:name, :attempts)

myArray = []

myArray << Test.new("zero", 0)

myArray << Test.new("one", 1)

myArray << Test.new("two", 2)

def testFunc(myArray)

	puts "rejecting three times"

	3.times do |count|

		puts "reject pass #{count}:"

		myArray.reject! do |element|

			if element.attempts > 3

				puts "Rejecting #{element.name} because we've attempted more than three times"

				return true

			else

				element.attempts += 1

				puts "Incremented attempt count of #{element.name} to #{element.attempts}"

				return false

			end

		end

	end

	puts "done rejecting"

	puts myArray

end

testFunc(myArray)

puts "done"

The output of this program will be

rejecting three times
reject pass 0:
Incremented attempt count of zero to 1
done

This isn’t what we expected. What’s going on?

In Ruby, the return statement is only valid inside methods. When you call return from inside the block, it is not evaluated as the return value of the block, nor the return value of the yielding function (reject!(), in this case), but of the surrounding method that initiated the whole thing in the first place. Why is this?

The underlying reason is that a block is not the same as a method. A method is a function explicitly declared using the def keyword. Recall from above that a block is defined inline and anonymously. In our case, the return statement inside the block is evaluated as the return value of our call to testFunc() on line 47, since that is the scope in which the block was created.

To understand this, you need to know that a block is similar to a closure and a closure is a function that has access to the scope in which it is declared. This is why the return statement becomes associated with the call to testFunc(). The reason that a block is only similar to a closure is that, unlike a closure, you cannot store or pass a block around as if it were a variable. Like a closure, it is forever married to the scope in which it is created. Unlike a closure, though, once you declare a block, its code can only be executed by the function it is passed to.

Using this new knowledge, we can rewrite testFunc() so that our solution produces the expected result.

def testFunc(myArray)

	puts "rejecting three times"

	3.times do |count|

		puts "reject pass #{count}:"

		myArray.reject! do |element|

			giveUp = element.attempts >= 3

			if giveUp

				puts "Rejecting #{element.name} because we've attempted more than three times"

			else

				element.attempts += 1

				puts "Incremented attempt count of #{element.name} to #{element.attempts}"

			end

			giveUp

		end

	end

	puts "done rejecting"

	puts myArray

end

Now our output looks like this

rejecting three times
reject pass 0:
Incremented attempt count of zero to 1
Incremented attempt count of one to 2
Incremented attempt count of two to 3
reject pass 1:
Incremented attempt count of zero to 2
Incremented attempt count of one to 3
Rejecting two because we’ve attempted more than three times
reject pass 2:
Incremented attempt count of zero to 3
Rejecting one because we’ve attempted more than three times
done rejecting
#<struct Test name=”zero”, attempts=3>
done

Some Subtleties of Ruby

I got wondering about Ruby’s garbage collector and it led me to some interesting tidbits about how Ruby does program pre-processing, object allocation, and memory management.

Below is a compilation of simple test cases and their corresponding object space stats.

ObjectSpace.each_object{} is a Ruby function that takes a class name and returns the number of objects of that type that currently exist in the object space. If no name is passed in, it yields the total number objects in the space.

For reference, I’m running ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]

Case 1 - List Object Space, no trailing spaces, no new lines
p ObjectSpace.each_object{};
6165

Case 2 - List Object Space, one new line following code
p ObjectSpace.each_object{};[CR\LF]

6165

Case 3 - List Object Space, two new lines
p ObjectSpace.each_object{};[CR\LF]
[CR\LF]
6166

Case 4 - List Object Space, two new lines, space
p ObjectSpace.each_object{};[CR\LF]
[CR\LF]
[SPACE]
6167

Case 4b - List Object Space, two new lines, two spaces, new line
p ObjectSpace.each_object{};[CR\LF]
[CR\LF]
[SPACE] [SPACE][CR\LF]
6167

The interesting things to note here is that when Ruby pre-processes your program, it will pre-allocate a String for any line that consists of only a [CR/LF] and/or one or more spaces.

Case 5 - Immediate GC
GC.start;p ObjectSpace.each_object{};
4126 (same result for multiple blank lines)

spaces.rb is a test file with a single [CR\LF] in it

Case 6 - GC, require
GC.start;require ’spaces.rb’;p ObjectSpace.each_object{};
4138 (same result for multiple blank lines)

Case 7 - require, GC
require ’spaces.rb’;GC.start;p ObjectSpace.each_object{};
4128 (same result for multiple blank lines)


Note that I did not include the ‘.rb’ suffix in the require statements for the following two tests.


Case 8 - GC, require

GC.start;require ’spaces’;p ObjectSpace.each_object{};
4154 (same result for multiple blank lines)

Case 9 - require, GC
require ’spaces’;GC.start;p ObjectSpace.each_object{};
4129 (same result for multiple blank lines)

You will get different results depending on when you garbage collect and when you pull in external files. Also, there is overhead if you do not explicitly declare the ‘.rb’ suffix of your required files.

The following tests were meant to examine a more realistic scenario. The file ’stdinc.rb’ was used as an include:

stdinc.rb
require ‘FileUtils’
require ‘log4r’

include Log4r

#setup directories
FileUtils.mkdir_p ‘logs’

#loggers
$log = Logger.new(’sys’)

#outputters
StdoutOutputter.new(’console’, :level=>ALL)
FileOutputter.new(’sysFile’, :filename=>’logs/sys.log’)

#assign outputters to loggers
Logger[’sys’].add ‘console’, ’sysFile’

Case 10 - require, GC
p ObjectSpace.each_object{};require ’stdinc.rb’;p ObjectSpace.each_object{};GC.start;p ObjectSpace.each_object{};
6166
17441
8545

Case 11 - GC, require
p ObjectSpace.each_object{};GC.start;p ObjectSpace.each_object{};require ’stdinc.rb’;p ObjectSpace.each_object{};
6166
4127
18909

Case 12 - GC, require, GC
p ObjectSpace.each_object{};GC.start; p ObjectSpace.each_object{};require ’stdinc.rb’;p ObjectSpace.each_object{};GC.start;p ObjectSpace.each_object{};
6166
4127
18909
8545

As you can see, the optimal case is to require your files and then garbage collect. There is no advantage to garbage collecting, pulling in external files, and then garbage collecting again. You can see that the interpreting of blank lines as Strings causes severe bloat even when only two common modules (log4r and FileUtils) are included.

Here’s a test to figure out the threshold for when the GC is automatically triggered

Case 13 - Check object space every 100 allocations and then do a final GC
p ObjectSpace.each_object{};GC.start;p ObjectSpace.each_object{};1000.times do 100.times do x = “hi there”;end;p ObjectSpace.each_object{};end;GC.start;p ObjectSpace.each_object{};

6166 [initial manual GC]
4127
4228


73110 [auto GC]
4180


36096 [final manual GC]
4127

Case 14 - No initial GC
p ObjectSpace.each_object{};1000.times do 100.times do x = “hi there”;end;p ObjectSpace.each_object{};end;GC.start;p ObjectSpace.each_object{};
6166
6267


12731 [auto GC]
4163


73045 [auto GC]
4193


29544 [final manual GC]
4127

If we let Ruby manage itself, an extra GC occurs. However, the final result is the same after our last manual collection.

Savage 2 Made Me Pop My Cherry

I can’t believe it happened to me…

Wandering the seedier parts of the internet yesterday (*cough*gamespy*cough*) I was solicited by an ad for an indie game called Savage 2: A Tortured Soul by S2 Games.

It was my first time. I’d never let an internet ad touch me like that before.

(Yes…I’ll justify it to myself by saying it was intentional…)

Ever.

(Seriously. I’ve been doing this how long? 10 years?)

I swore I was saving myself for Puzzle Quest 2.

(But I’m into the scene, so I figured, “what’s the harm?” )

I never thought it could happen to me.

But now there’s something growing inside of me.

(Oh god…)

My first reaction?

(Not what you might expect.)

“For an indie shop? Not bad.”

My girlfriend’s reaction?

“Looks just like Warcraft…”

(Oh god…she knows…)

Me: “Naw…”

(play it cool…)

Her: “…JUST like Warcraft.”

(Make it a quick confession.)

I have to hand it to her. She knows me. Lacking 20 years of pedantic gamer nitpicking to jade her, she was pretty spot on. As a stickler for details when it comes to things like this, I won’t say I was precisely wrong for what I did, and neither was she precisely right about how she felt about it, but…

…as it turns out, we were both right! And she still loves me despite my indiscretion. Savage 2 definitely tries to pay homage to the Warcraft franchise in look (WoW) and feel (Warcraft 3), but when it comes to actual implementation…?

Well, read on…

The Big Idea
Savage 2 is touted as a hybrid RTS/FPS/MMO(RPG?). Flowery story descriptions aside, it’s one team versus the other and you’re either the Humans or the Beasts. Players are segregated into one of two roles: commander or squad member. One player is the commander and plays the game as if it were an RTS. He places buildings, doles out buffs and debuffs, pings the map, and gives orders. The other players choose what unit to be and are presented with a FPS or 3rd-person perspective of the battlefield depending on what ability is selected. Attacks and abilities are executed as in MUMORPUGER’s via hotkeys or clicking on icons.

There is no persistence to the world itself since each match is played on a specific map. Levels, statistics, and special items specific to your account accrue and persist as you take part in matches, however.

The game is $30 and has no monthly fees.
Demo users are given 5 hours of play time to check things out but are gimped in the following ways

  • longer respawn time (20-30 seconds)
  • cannot be the commander
  • no tracking of stats and achievements
  • cannot access certain units and abilities

The Gameplay
The ultimate goal is to destroy the opponent’s main stronghold.

After joining a team and selecting a squad, you choose a unit to spawn as and spend gold to outfit yourself with items. Items include the standard array of health, magic, and stamina potions as well as armor, speed, and attack buffs. As you defeat enemies you will gain experience and personal gold that can be spent on either items or to play as higher-tier units. This is different from “team gold” which is gained by controlling gold mines. Team gold is used by the commander to carry out his duties.

Speaking of which…

Herding Cats
I feel really bad for the commander. He just has no way to enforce order or compliance. The commander can ping the map, or type out an order (that you can’t see anyway because the font size and color scheme is so poor) but ground troops have no compulsion to carry out the order. Occasionally you’ll get a “move here” or “attack here” but the UI doesn’t do a good job of indicating precisely where “here” is. It would be much better if orders were enforced and rewarded or at least encouraged by game mechanics.

I suspect that a team with a good commander organizing tactics at a gross level could be quite successful, but I still wonder how interesting things would be for the players on the ground even then. Because everything is so fast-paced, there’s no opportunity to appreciate anyone pulling off an amazing counter or attack. Winning a skirmish feels mostly like chance. Whichever team has superior numbers usually seems to win. Getting the group to rally and not run back to die one-by-one is nearly impossible in a demo pick up game.

The worst part was that I never felt like I had been part of a team effort, even after winning several matches. I didn’t come away feeling like any individual’s contributions were either important or detrimental. There were no MVPs. There were no noobs. Savage 2 is the video game equivalent of mango Dole Whip. You go into the experience expecting something great but come away feeling like there was a hole in it, like a certain unnameable something was missing.

The Execution
This game reminds me so much of Vanguard: Saga of Heroes, it’s like reliving a bad nightmare. It’s a huge type of game. Huge potential, huge ambitions, and lots of let downs. I wanted to enjoy this game really badly because the idea is just so great.

Under the hood, S2 uses a proprietary engine called K2 but at first I thought they must be using the same engine that Vanguard did (Unreal 2). You can take this as a compliment, since the Unreal engine is amazing; however, if Vanguard showed me anything it was that how you use your technology is just as important as what technology you choose to use. Animations are somewhat sloppy, collision detection can be frustrating, play control is unrefined, textures and shaders are a little bland and repetitive, and the UI gets in the way. Conceptually, the units are somewhat generic, but graphically there’s nothing about them or their abilities to really make them “pop” in the heat of battle.

For an RTS that expects players to control the actions of individual units in real time, the gameplay is way too fast and unrefined. There’s a rock-paper-scissor type of melee combat that the left, right, and middle mouse buttons map to. Block beats attack, interrupt beats block, and attack beats interrupt. Throw in 5 or 6 abilities per unit with poorly written descriptions, plus ranged attacks, plus items, and things get unwieldy pretty fast.

More often that not, I found myself feeling helpless, overwhelmed, and ultimately apathetic. What spells are being cast at me? What does that buff do? Which one of them is the healer? Where the hell is the rest of my team? Why the fuck isn’t anyone healing me? What the hell does this ability do? Whereas an RTS gold-standard like Warcraft 3 is instantly intuitive, Savage 2 is just the opposite. Even with your team at your back, your death usually comes so quickly from being ganked that you’ll grow frustrated at feeling like you have no real control over the outcome of encounters. One major issue is that you’re forced into mouse-look mode, so you can’t mouse over anything to figure out what the hell is happening. I found myself reminiscing…A strange game. The only winning move is not to play.

The Verdict
Despite all the above, I’ll be keeping my eye on Savage 2.

It could be great. But right now it’s not.

I love indie gaming but I hate making excuses for the indie scene just because it’s the indie scene. That’s what makes it so hard about a studio having such a great idea and pulling it off so haphazardly. S2 reached for the stars and delivered an adequate beta version. Either 6 more months in the cooker, or striving for more depth and polish instead of breadth of unfinished features would have made a big difference.

Definitely give the demo a shot, but wait for a few more patches before plunking down your $30.