Wednesday, November 19, 2014

The dining philosophers problem with Ruby and Celluloid

Celluloid describes itself as a concurrent object oriented programming framework for Ruby which lets you build multithreaded programs out of concurrent objects just as easily as you build sequential programs out of regular objects. It is an actor-based framework for Ruby inspired by Erlang. Tony Arcieri, the creator of Celluloid, suggested on a Ruby Rouges episode to try to solve one of the basic concurrency problems using it if you want to learn using Celluloid. So let's do that and solve the dining philosophers problem. Wikipedia defines the problem likes this

Five silent philosophers sit at a round table with bowls of spaghetti. Forks are placed between each pair of adjacent philosophers [...] Each philosopher must alternately think and eat. However, a philosopher can only eat spaghetti when he has both left and right forks [...] The problem is how to design a discipline of behavior (a concurrent algorithm) such that each philosopher won't starve; i.e., can forever continue to alternate between eating and thinking assuming that any philosopher cannot know when others may want to eat or think.

We will model the problem with two classes: Philosopher and Fork, and they will both be implemented as Celluloid Actors.

To create a concurrent object in celluloid we create a normal Ruby class and include the Celluloid module in it. Let's create a skeleton for the Philosopher class and make it an Actor.

require 'celluloid'

class Philosopher
  include Celluloid

  def initialize(number)
    @number = number
  end

  def start
    send([:eat, :think].sample)
  end

  private

  def eat
    puts "Philosopher #{@number} eating"
    sleep(1)
    think
  end

  def think
    puts "Philosopher #{@number} thinking"
    sleep(1)
    eat
  end
end

The Philosopher will randomly either think or eat. When we create instances of this class they will actually be instances of Celluloid::ActorProxy, and run in their own threads. To be able to run multiple methods concurrently we have to invoke them chained after the async method that the Celluloid module provides, this is Celluloids way of passing a message to an Actor.

  1.9.3p429 :001 > p1 = Philosopher.new(1)
   => #<Celluloid::ActorProxy(Philosopher:0x3fcfe141f050)>
  1.9.3p429 :002 > p2 = Philosopher.new(2)
   => #<Celluloid::ActorProxy(Philosopher:0x3fcfe1413cc8)>
  1.9.3p429 :003 > p1.async.start; p2.async.start
Philosopher 1 eating
 => nil
Philosopher 2 thinking1.9.3p429 :004 >
Philosopher 1 eating
Philosopher 2 thinking
Philosopher 1 thinking
Philosopher 2 eating

The output is mixed up with the console output because the output is happening in separate threads. To solve the real problem at hand we need some forks as well and we need to make sure that only one Philosopher is using each fork at any given time. We can achieve this in a number of different ways. But the main idea is that we need to synchronize access to the shared recourses, i.e. the forks. In classical concurrent programming we would use a lock or mutex to make sure that only one thread (Philosopher) is using the shared resource (Fork) at once. But with Actors we generally don't need to use locks but instead rely on passing messages (call methods chained after the async method with Celluloid), between the actors in the system.

We will use the resource hierarchy solution to avoid deadlocks. All forks will be numbered and each philosopher has to pick up the lowest numbered fork first.

require 'celluloid'

class Fork
  include Celluloid

  def initialize(number)
    @free = true
    @number = number
    @waiting = :none_waiting
  end

  def pick_up(philosopher)
    if @free
      @free = false
      philosopher.async.fork_picked_up(@number)
    else
      @waiting = philosopher
    end
  end

  def put_down
    if @waiting == :none_waiting
      @free = true
      @waiting = :none_waiting
    else
      @waiting.async.fork_picked_up(@number)
    end
  end
end

The Fork keeps track of both weather if it currently in use and if there is a Philosopher waiting on it. When it is free to pick up it sends the #fork_picked_up message to the Philosopher, passing in its own number so that the Philosopher knows which fork it managed to get hold of. The Philosopher skeleton we saw earlier needs a few changes, before starting to eat it needs to pick up both its forks and wait for the #fork_picked_up message.

NUM_PHILOSOPHERS = 5

class Philosopher
  include Celluloid

  def initialize(number, forks)
    @number = number
    @forks = forks
    @has_eaten = false
  end

  def start
    send([:eat, :think].sample)
  end

  # To be able to ask all philosopher if they got to eat
  def hungry?
    !@has_eaten
  end

  def fork_picked_up(fork_number)
    if fork_number == first_fork
      @forks[second_fork].async.pick_up(Actor.current)
    else
      ready_to_eat
    end
  end

  private

  def think
    puts "#{name} thinking"
    random_sleep
    eat
  end

  def eat
    @forks[first_fork].async.pick_up(Actor.current)
  end

  def ready_to_eat
    puts "#{name} eating"
    @has_eaten = true
    random_sleep
    put_down_forks
    think
  end

  def put_down_forks
    @forks[first_fork].put_down
    @forks[second_fork].put_down
  end

  def name
    "Philosopher #{@number + 1}"
  end

  # Do the selected activity for a random amount of time
  def random_sleep
    sleep((1 + rand(10)) * 0.1)
  end

  def first_fork
    @number % (NUM_PHILOSOPHERS - 1)
  end

  def second_fork
    [4, @number + 1].min 
  end
end

To ease running of the simulation we can add a small Manager class that creates the necessary instances and starts the simulation.

class Manager
  include Celluloid

  def run
    puts "Philosophers sitting down to eat"
    Celluloid.logger = nil # To avoid Celluloid debug output
    forks = (0...NUM_PHILOSOPHERS).map { |i| Fork.new_link(i) }
    philosophers = (0...NUM_PHILOSOPHERS).map { |i| Philosopher.new_link(i, forks) }

    # Start the simulation
    philosophers.each { |p| p.async.start }
    sleep 5 # Let the simulation run for 5 seconds
    puts "Time to break up the party"

    # Check if all philosophers have gotten to eat
    puts "All philosophers have eaten? #{philosophers.none? { |p| p.hungry? } ? 'Yes' : 'No'}"
  end
end

We put all these classes in a file called philosophers.rb and one more line to get everything going.

Manager.new.run

We can now run the simulation from the console.

> ruby philosophers.rb
  Philosophers sitting down to eat
  Philosopher 1 thinkingPhilosopher 3 thinking

  Philosopher 4 thinking
  Philosopher 2 eating
  Philosopher 5 eating
  Philosopher 5 eating
  Philosopher 2 thinking
  Philosopher 3 eating
  ...
  Time to break up the party
  All philosophers have eaten? Yes

To see that it is the ordering in which the philosophers pick up their forks that ensures that we don't get into a deadlock, we can change the Philosophers first_fork and second_fork methods to always return the left fork first then the right fork.

class Philosopher
  include Celluloid

  ...

  def start
    eat   # Try to get into a deadlock by making all philosophers start with eating
  end

  ...

  def fork_picked_up(fork_number)
    if fork_number == first_fork
      sleep(1)  # Sleep after first fork picked up to allow all philosophers to try to pick up their first fork
      @forks[second_fork].async.pick_up(Actor.current)
    else
      ready_to_eat
    end
  end

  private

  ...

  def first_fork
    @number
  end

  def second_fork
    (@number + 1) % NUM_PHILOSOPHERS
  end
end

If we run the simulation again with this new code we will immediately get into a deadlock. So Actors cannot ensure that we never get a deadlock, and we still have to be careful when designing concurrent programs that share resources. But Actors (and Celluloid) certainly makes it easier to write correct concurrent code.

Wednesday, June 19, 2013

Sidekiq thread dump on Heroku

We have occasionally been seeing jobs getting stuck in Sidekiq. A worker thread has started the job but then gotten stuck executing it. The Sidekiq documentation suggests that you send the Sidekiq process the TTIN signal which will make Sidekiq show backtraces for all worker threads. But since we are running our app on Heroku there is no way of logging in to a certain dyno or send its processes a signal from the command line. The solution around this that we came up with was to send the signal from witin a Sidekiq job. So we created a simple class that could be run as a Sidekiq job.
class SendTTIN
  include Sidekiq::Worker

  def perform
    pid = Process.pid
    `kill -s ttin #{pid}`
  end
end
Open a console on Heroku and enqueue the job with Sidekiq.
irb(main):001:0> SendTTIN.perform_async()
Then check the logs for the backtraces of all Sidekiq threads.

Saturday, October 22, 2011

Buildr

(earlier posted on the Avega Group Blog)
Buildr is a build tool for building Java projects designed to be a drop in replacement for Maven. It uses the same default directory structure as Maven with source files in src/main/java, tests in test/main/java, etc. And it downloads dependencies from maven repositories. So why would you want to learn yet another build tool when the two standard ones (ant and maven) are so dominating? For me it was because I was tired of writing xml files and wanted a regular programming language for specifying our builds. Buildr is built on top of Rake "the ruby make", and is a internal ruby dsl. Rake integrates nicely with ant and can call ant tasks, see this blog for an example of how to translate an existing ant file to Rake. To get a feeling for how buildr works we will walk through how to build a sample Java project with Buildr. We will use an EJB 2 application from sun oracle called Duke's Bank. I modified it slightly to get the different parts of the application in separate sub-projects. The application has a simple web gui, some ejbs and backend code and a standalone application client. You can download the sample application code with a working Buildr buildfile (named buildfile.demo, rename it buildfile if you don't want to write your own following the instructions in this blog) from my github repository. I have organized the code in four project folders, AppClient, BankEar, BankEjb and BankWeb. To get started install Buildr by following the installation instructions for your platform on Buildr's home page: http://buildr.apache.org/installing.html Open a terminal window / command prompt and cd to the directory containing the source code for the project. Run the buildr command and you should get a message from Buildr.
To use Buildr you need a buildfile. Do you want me to create one?:
1. From directory structure
2. Cancel
?  
Select 1 to let Buildr generate a buildfile for you. This will create a new file called buildfile in your current directory that looks something like this:
# Generated by Buildr 1.4.5, change to your liking
# Version number for this release
VERSION_NUMBER = "1.0.0"
# Group identifier for your projects
GROUP = "bank"
COPYRIGHT = ""

# Specify Maven 2.0 remote repositories here, like this:
repositories.remote << "http://www.ibiblio.org/maven2/"

desc "The Bank project"
define "bank" do

  project.version = VERSION_NUMBER
  project.group = GROUP
  manifest["Implementation-Vendor"] = COPYRIGHT

  define "AppClient" do
  end

  define "BankEjb" do
  end

  define "BankWeb" do
  end

end
From the buildfile we can see that each of our sub-projects gets a 'define' block, except for the 'BankEar' project that does not contain any code, but we will manually add a define block for it later. Following Maven conventions Buildr expects the source code to be located in src/main/java, but the sample application does not follow this convention. So if we run buildr in the top directory, now with the generated buildfile in place, Buildr will not find anything to compile and will just exit saying everything went well. To tell Buildr that we have a non-default project layout we define a Layout before the 'define "bank"' line and specify the location of our source code, like so:
ejb_layout = Layout.new
ejb_layout[:source, :main, :java] = 'src'

We then tell Buildr to use this layout for the BankEjb project:

define "BankeEjb", :layout=>ejb_layout
  package(:jar)
end
We also added a line to tell Buildr to package the BankEjb project as a jar file. If we run buildr again we can see that it now finds our source code and tries to compile it. The problem is just that it is missing some necessary jar dependencies. We can either add these jar files as maven style dependencies with group-id, artifact-id and version, for example to add a compile time dependency to hamcrest-core 1.1 we can add the following line to our BankeEjb project:
compile.with 'org.hamcrest:hamcrest-core:jar:1.1'
When Buildr sees this it will try to download the jar from the list of maven repositories specified in our buildfile. The other option is to add a reference to an existing jar file, or directory of jar files, on disk. Which is the approach we will use in this example:
compile.with Dir[path_to('lib/*.jar')]
This will compile the BankEjb project with all jar files in the lib/ directory. If we run buildr again it now compiles the code without errors. Similar to maven Buildr has different tasks we can run, the default being 'build':
You can also see the available tasks with 'buildr help:tasks', and the projects Buildr knows about with 'buildr help:projects'. So by just running Buildr the build task is run. To also have the BankEjb jar file created we have to run the package task with, 'buildr package'. The generated jar file will be placed in the 'target/main' directory. To continue we add a layout for the BankWeb project, specifying both a source/main/java directory as well as a source/main/webapp directory. We also add a package(:war) directive since we want Buildr to package this as a war file. If we run 'buildr clean package' we get some errors in the output telling us that the web project depends on the ejb project. So lets add it as a dependency. We do this like with did in the ejb project for external jar dependencies.
compile.with project('BankEjb')
We can also see that the web project depends on the same jars as the ejb project, to add them we tell Buildr to use the compile dependencies from BankEjb when compiling BankWeb:
compile.with project('BankEjb'), project('BankEjb').compile.dependencies
With this in place both projects compiles without errors. If we look in the src folder of the BankWeb project we will find three properties files that are not included in the generated war file. (You can check by unziping the generated war file located in BankWeb/target/main). To get the properties files packaged in the war we have to tell Buildr about them.
package(:war).include path_to(:source, :main, :java, '/**/*properties'), :path=>'WEB-INF/classes'
The :path argument is the location inside the war file where we want the properties files copied to. path_to is a Buildr function that expands its argument to a full path. The process is similar for the AppClient project and the relevant part of the resulting buildfile looks like this.
define "AppClient", :layout=>ejb_layout do
    compile.with project('BankEjb'), project('BankEjb').compile.dependencies
    package(:jar).include path_to(:source, :main, :java, '/**/*properties'), :path=>'appclient'
  end
Finally we want to add a project definition for the ear project and package the three previous projects in it.
define 'BankEar'
  package(:ear).add :jar=>project('AppLicent')
  package(:ear).add :ejb=>project('BankEjb')
  package(:ear).add :war=>project('BankWeb'), :context_root=>'bank'
  package(:ear).include path_to('conf/*'), :path=>''
end
You can read more about Buildr's ear package here. You can also see that we added a line to include the configuration files in the conf/ directory. The only thing left now is to set the display name for the application and to define some security roles.
  package(:ear).display_name = 'JBossDukesBank'
  package(:ear).security_roles << {:name=>'BankAdmin'}
  package(:ear).security_roles << {:name=>'BankCustomer'}
That's it. The resulting buildfile looks like this.
VERSION_NUMBER = "1.0.0"
# Group identifier for your projects
GROUP = "bank"
COPYRIGHT = ""

# Specify Maven 2.0 remote repositories here, like this:
repositories.remote << "http://www.ibiblio.org/maven2/"

ejb_layout = Layout.new
ejb_layout[:source, :main, :java] = 'src'

web_layout = Layout.new
web_layout[:source, :main, :java] = 'src'
web_layout[:source, :main, :webapp] = 'WebContent'

desc "The Bank project"
define "bank" do

  project.version = VERSION_NUMBER
  project.group = GROUP
  manifest["Implementation-Vendor"] = COPYRIGHT

  define "BankEar" do
    package(:ear).add :jar=>project('AppClient'), :path=>''
    package(:ear).add :ejb=>project('BankEjb'), :path=>''
    package(:ear).add :war=>project('BankWeb'), :path=>'', :context_root=>'bank'
    package(:ear).include(path_to('conf/*'), :path=>'')
    package(:ear).display_name = 'JBossDukesBank'
    package(:ear).security_roles << {:name=>'BankAdmin', :id=>"admin", :description=>"Administrator role"}
    package(:ear).security_roles << {:name=>'BankCustomer', :id=>"customer", :description=>"Customer role"}
  end

  define "AppClient", :layout=>ejb_layout do
    compile.with project('BankEjb'), project('BankEjb').compile.dependencies
    package(:jar).include path_to(:source, :main, :java, '/**/*properties'), :path=>'appclient'
  end

  define "BankEjb", :layout=>ejb_layout do
    compile.with Dir[path_to('lib/*.jar')]
    package(:jar)
  end

  define "BankWeb", :layout=>web_layout do
    compile.with project('BankEjb'), project('BankEjb').compile.dependencies
    package(:war).include path_to(:source, :main, :java, '/**/*properties'), :path=>'WEB-INF/classes'
    package(:war).with :libs=>path_to(:source, :main, :webapp, 'WEB-INF/lib/*')
  end

end
If you want to deploy and run the application we first have to create the database schema. We can add a custom task for this in our buildfile. The necessary sql scripts to create the database and populate it with some sample data are located in the sql directory. We begin by adding a task to create the database using the hsql ScriptTool. Buildr tasks are defined with the Rake task keyword.
task :create_db do
    system "java -cp sql/hsqldb.jar org.hsqldb.util.ScriptTool -url jdbc:hsqldb:hsql: -database //localhost:1701 -script sql/hsql-create-table.sql"
end
system is a ruby function that runs an external program, in this case java. The create_db task creates the database. To populate it with data we need to run the hsql-insert.sql script with the hsql ScriptTool. Lets at the same time extract the system call to a ruby function.
task :create_db do
  run_hsql_script('sql/hsql-create-table.sql')
end

task :populate_db do
  run_hsql_script('sql/hsql-insert.sql')
end

def run_hsql_script(script)
  system "java -cp sql/hsqldb.jar org.hsqldb.util.ScriptTool -url jdbc:hsqldb:hsql: -database //localhost:1701 -script #{script}"
end
For this to work we need to have a hsql database running on localhost listening on port 1701. JBoss can be configured to start a hsql database upon startup. The sample application we are using is tested and works with JBoss 4.0.5, download it here. To configure hsql modify the server/default/deploy/hsqldb-ds.xml file and make sure the lines where hsql is configured to run in server mode, and can be access over tcp, are uncommented (3 places needs to be changed). If we start JBoss with the bin/run.sh script we can now run the create_db and populate_db tasks:
> buildr bank:create_db bank_populate_db
Finally if we want to deploy the generated ear file we need to copy it to the JBoss deploy dir (/server/default/deploy/), either manually or with a buildr task. You will find a task for this in the buildfile.demo file. This step by step guide has been an attempt to show how easy it is to start using Buildr to build your application. Take a look at the Buildr documentation at buildr.apache.org for a complete reference of Buildr's features. Unfortunately development seems to have stopped on Buildr the last couple of months. And nothing much is happening on the mailing lists either. So we will see what happens. If development doesn't start again soon Gradle might be a better choice if you want to use a regular programming language to build your application. At the moment there is much more activity going on there. Read more about it here.

Tuesday, February 15, 2011

DbUnit tests with Spring and nested transactions

In my current project we are using DbUnit to set up test data for our system tests. It's a J2EE application that uses Spring and Hibernate. When we started with DbUnit we set it up to have one xml file per test class and configured DbUnit to insert the data in the xml file in a JUnit4 @Before annotated method. But this was kind of slow as the data gets reinserted before each test case. Dbunits practice of commiting its data also meant that even if we extended springs AbstractTransactionalJUnit4SpringContextTests (ATJ4SCT) the dbunit data is not rolled back. Dbunit suggests using the Clean_Insert DatabaseOperation and to have empty table rows in the xml files for tables we dont insert data into but want to have cleared by dbunit. We tried this, but it was cumbersome to always have to put in the empty table rows in the correct order to avoid foreign key constrains on delete. And on a few occasions we got false positives from test cases that passed because an earlier test had inserted data that we had forgotten to remove in the current test.
To get around this we wanted to setup DbUnit to not commit its data, so that it would automatically be rolled back if we used Springs ATJ4SCT. To do this we created a new class extending DbUnits DatabaseDataSourceConnection.



And a new class extending java.sql.Connection that delegate all the methods to the passed in connection except for the commit method that we set to do nothing.



DataSourceUtils is a Spring class that only closes the DataSource if no one else is using it.
We can use this class in our testcode like so.



With this setup we could instruct DbUnit to insert the data before each test and not have to worry about putting in empty table statements in our DbUnit xml files. But the test-cases was of course still quite slow to run. It would be nice if we could have DbUnit insert its data before the test class starts and rolled back after all test-cases in the class have finished but still have each test-case run in isolation, in its own transaction.
This is exactly what nested transactions does. It starts a new transaction inside an existing one, that can be rolled back independently of the surrounding transaction but that still sees all data written by the outer transaction. The only problem left was how to start the transaction and insert the test data before each test-class. In a @BeforeClass static method we don't have access to the Spring context. And therefore no access to the Spring configured DataSource or TransactionManager.
But Spring has the notion of TestExecutionListeners, ATJ4SCT uses a TransactionalTestExecutionListener to start and roll back a transaction around each test-method. The TestExecutionListener interface has four methods: beforeTestMethod, afterTestMethod, beforeTestClass and afterTestClass. So by creating our own TestExecutionListener and implement the -testClass methods we will have access to the Spring context and a place to do work before and after each test-class. Our TestExecutionListener looks for a DbUnit xml file with the same name as the test class that is running, to insert as test data. Most of the code is copied from Springs TransactionalTestExecutionListener.



In our base class for DbUnit test we specified to run the tests with our new TestExecutionListener and to start a nested transaction for each test-case with the Spring @Transactional(propagation=Propagation.NESTED) annotation. The only problem left is that the Hibernate session is not cleared before the outer transaction ends. So in our base class we add a call to clear on the Hibernate session after each test-case.



Thats it. All integration tests that hits the database can now just extend this base class. If you want to have another naming convention for your DbUnit xml files than the class-name, its easy to create an annotation where you can specify the filename and look for this annotation in the DbUnitTransactionPerTestClassListener class.
Feel free to leave comments about this approach.
How do you handle testdata in your own projects?

Thursday, November 19, 2009

Extreme OOP Exercise in Ioke

(The solution in this article is probably not the best way to solve this exercise in Ioke, but read on if you want. A better way is probably something along the lines in this blog post.)

I just reread a pdf describing extreme oop, you can read it here, inspired by the Object Calestenetics chapter in The Thoughtworks Antology. I like the idea and decided to try it out using Ioke. To do this we need to modify the rules a bit. As originaly stated they read.

Extreme OOP Rules
1. Use only one level of indentation per method.
2. Donít use the else keyword.
3. Wrap all primitives and strings.
4. Use only one dot per line.
5. Donít abbreviate.
6. Keep all entities small.
7. Donít use any classes with more than two instance variables.
8. Use first-class collections.
9. Donít use any getters/setters/properties

My try at translating them to apply to the Ioke syntax yielded this.

Extreme OOP Rules for Ioke
1. Use only one level of indentation per method.
2. Donít use the else argument to the if method.
3. Wrap all primitives and strings.
4. Use only one method call per line.
5. Donít abbreviate.
6. Keep all entities small.
7. Donít use any kind with more than two inactivable cells (cells that are not methods).
8. Use first-class collections.
9. Donít access another objects inactivable cells directly or via dumb getter/setters.

The exercise in the pdf is to write a simple interpreter for (something similar to) Commodore 64 BASIC. The requirements are given as stories, that can be translated nicely into ISpec. So lets start with the first story.

Story: An empty program produces no output. Acceptance:
input:
(empty)
output:
(empty)

To start with we won't bother with the ui. I first translated this into the following ISpec code.
describe(Interpreter,
it("should give no output given an empty program",
Interpreter input("") should == ""
)
)

But then I remembered that all Strings and primitives should be wraped. So we need a class that represent programs. Lets try this instead.
use("ispec")
describe(Interpreter,
it("should give no output given an empty program",
Interpreter input(Interpreter TextProgram fromText("")) should == ""
)
)

It fails because there is no Interpreter kind, so lets create the Interpreter and TextProgram kinds and the input method.
Interpreter = Origin mimic do (
input = method(textProgram,
"" ; for now we just return an empty string
)

TextProgram = Origin mimic do(
fromText = method(text,
self ; just return self
)
)
)

If we add a use clause for this new file to the iSpec test file the test passes, so lets move on to the next story.

Story: A bare "print" statement produces a single newline. Acceptance:
input:
PRINT
output:
(a single newline)

Translated to ISpec.
it("should produce a single newling given a bare \"print\" statement",
Interpreter input(Interpreter TextProgram fromText("PRINT")) should == "\n"
)

The specification fails and to make it pass we need to save the program in TextProgram and update the input method. To begin with we just check if the program is empty or not and let all non-empty programs return "\n".
Interpreter = Origin mimic do (
input = method(textProgram,
if(textProgram text == "",
return "")
"\n"
)

TextProgram = Origin mimic do(
initialize = method(text,
@text = text
)
fromText = method(text,
self mimic(text)
)
)
)

As we are not allowed to use the else keyword, we use an if as a guard clause for empty inputs and return "\n" for all other cases. The test passes, but we have broken the rule that we shuld not access an objects cells directly, i.e. the text cell in the textProgram object. So lets move the check if the program is empty into the TextProgram kind.
TextProgram = Origin mimic do(
initialize = method(text,
@text = text
)
fromText = method(text,
self mimic(text)
)
empty? = method(@text empty?)
)

And use this method from the input method.
input = method(textProgram,
if(textProgram empty?,
return "")
"\n"
)

The test still passes, and there is no real need to refactor the code, so lets see where the next story brings us.

Story: A "print" statement can have a constant string as an argument. The output is the constant
string. Acceptance:
input:
PRINT "Hello, World!"
output:
Hello, World!

Wich translates to the following ISpec code.
it("should output the content of a given constant string passed to print",
Interpreter input(Interpreter TextProgram fromText("PRINT \"Hello, World!\"")) should == "Hello, World!\n"
)

To pass this test we need to modify the input method again. Lets start by making the test pass without worrying about the rules.
input = method(textProgram,
if(textProgram empty?,
return "")
if(textProgram text == "PRINT",
return "\n")
"Hello, World!\n"
)

This passes all the tests, but wat we really need is some function that can handle the print statement. Lets create a Print kind to handle print statements.
Print = Origin mimic do (
initialize = method(argument,
@argument = argument
)
execute = method(@argument asText + "\n")
)

We added an initialize method that saves the argument in the argument cell and an execute method that we can call to get the result of executing the statement. To extract the argument to the Print statement we add a argument method to the TextProgram kind and wrap the argument to Print in a new Argument kind.
TextProgram = Origin mimic do (
...
argument = method(
Interpreter Argument mimic(@text split rest join(" "))
)
)

Argument = Origin mimic do(
initialize = method(text,
@text = text
)
asText = method(
@text[1...-1] ; remove the "" surrounding text arguments
)
)

To be able to handle all return values from input as objects with an execute method we also create a base kind Program to handle the empty program.
Program = Origin mimic do (
execute = method("")
)

With these new kinds in place, and a "first" method on TextProgram we can change the input method.
input = method(text,
program = Interpreter Program mimic
if (text first == "PRINT",
program = Interpreter Print mimic(Interpreter Argument mimic(text rest)))
program execute
)

TextProgram = Origin mimic do(
initialize = method(text,
@text = text
)
fromText = method(text,
self mimic(text)
)
first = method(
@text split[0]
)
rest = method(
@text split rest join(" ")
)
)

As you can see we also replaced the argument method in TextProgram with a rest method, so that it is more in alignment with the first method. Now the input method is responsible for creating objects for our program and their argument, and the TextProgram kind only deals with text processing.
The test still passes and I don't feel the need to refactor the code any more right now. So lets move on to the next story.

Story: Two or more statements in a sequence are executed one after the other
PRINT "Hi"
PRINT "There"
PRINT "!"
output:
Hi
There
!

Translated to an ISpec specification.
it("should execute consequetive statements one after the other",
Interpreter input(Interpreter TextProgram fromText("PRINT \"Hi\""),
Interpreter TextProgram fromText("PRINT \"There\""),
Interpreter TextProgram fromText("PRINT \"!\"")) should == "Hi\nThere\n!\n"
)

The test fails. To make it pass all we need to do is invoke our current logic in a loop, collect the result and return it as a string.
input = method(+textPrograms,
textPrograms map(text,
program = Interpreter Program mimic
if (text first == "PRINT",
program = Interpreter Print mimic(Interpreter Argument mimic(text rest)))
program execute
) join
)

But this introduces another level of indentation in the input method and breaks the first rule "Use only one level of indentation per method." So we need to refactor this code. Lets simply introduce a handleLine method that we use from the original input method.
input = method(+textPrograms,
textPrograms map(text,
handleLine(text)
) join
)

handleLine = method(text,
program = Interpreter Program mimic
if (text first == "PRINT",
program = Interpreter Print mimic(Interpreter Argument mimic(text rest)))
program execute
)


The next story introduces numbers as argument to PRINT.

Story: The "print" statement can output number constants.
PRINT 123
output:
123
PRINT -3
output:
-3

Translated to ISpec we get.

it("should output numbers passed as argument to PRINT",
Interpreter input(Interpreter TextProgram fromText("PRINT 123"),
Interpreter TextProgram fromText("PRINT -3")) should == "123\n-3\n"
)

As expected the test fails. To handle this case we need to change the way we handle the argument to the Print statement. More specifically we need to change the asText method in the Argument kind so that it removes the quotation marks surrounding text and leave numbers untouched.
Argument = Origin mimic do (
...
asText = method(
if(@text[0..0] == "\"", ; a text argument
return @text[1...-1]) ; remove the "" surrounding text arguments
return @text
)
)



This makes the test pass, and we are still following all the rules so lets move on to the next story.

Story: A single letter is a variable. The print statement can print its value. The default value for a
variable is 0

PRINT A
output:
0

Translated to ISpec.
it("should treat single letters as variables with 0 as default value",
Interpreter input(Interpreter TextProgram fromText("PRINT A")) should == "0\n"
)

The test fails. All we have to do to make this story pass is to replace single letters outside of strings with 0. We don't need to worry about remembering variable values, yet. So lets just parse the argument and replace single letters outside of strings with 0.
Print = Program mimic do (
...
execute = method(
@argument = @argument evaluate
@argument asText + "\n"
)
)

In Argument we create the evaluate method and add a textArgument? method.
Argument = Origin mimic do(
...
textArgument? = method(
@text[0..0] == "\""
)
evaluate = method(
if(@textArgument?,
return @)
res = @text split map(expr,
res = #/-?\d+/ =~ expr
if(res,
return expr)
return 0
) join
Interpreter Argument mimic(res)
)
)

This implementation makes all the test pass but it doesn't look very nice, and we are breaking the rule about not having more than 1 method call per line with the line "res = @text split map(expr,". To clean up the code we start with creating kinds for the different types of arguments we accept to Print. This also means that we have to change the creation logic for Arguments, instead of mimic:ing Arguments with a text arguments it feels more appropriate to create a fromText method in Arguments that we can call from the handleLine method.
handleLine = method(text,
program = Interpreter Program mimic
if (text first == "PRINT",
program = Interpreter Print mimic(Interpreter Argument fromText(text rest)))
program execute
)

The Argument kind.
Argument = Origin mimic do (
initialize = method(value, @value = value)
fromText = method(value,
cond(
value empty?, Interpreter Argument mimic(value),
textArgument?(value), Interpreter TextArgument mimic(value),
numberArgument?(value), Interpreter NumberArgument mimic(value),
Interpreter VariableArgument mimic(value)
)
)
textArgument? = method(value, value[0..0] == "\"")
numberArgument? = method(value, #/-?\d+/ =~ value)
evaluate = method(@value)
)

TextArgument = Argument mimic("") do (
evaluate = method(@value[1...-1])
)

VariableArgument = Argument mimic("") do (
evaluate = method(0)
)

NumberArgument = Argument mimic("") do (
)

In the argument kinds mimic:ing Argument we pass in a default value of "". When one of the sub kinds are mimic:ed with an argument the initialize method in Argument will be called and the value cell is set to the passed in value.
iik> Interpreter NumberArgument
+> Interpreter NumberArgument_0x114080D:
kind = "Interpreter NumberArgument"
value = ""

iik> Interpreter NumberArgument mimic(3)
+> Interpreter NumberArgument_0x10A1F17:
value = "3"

The code looks better and the tests still pass. The main problem now is that the cond expression in the fromText method in Argument breaks the rule that we should not use else statements. We need to somehow determine what kind of argument we are dealing with. One way to do this without using a cond would be to use a Dict with regular expressions we want to match against and the kind we want to create if the expression matches the passed in text value.
Argument = Origin mimic do(
...
typeDict = {}(#/^$/ => Interpreter Argument mimic(""),
#/^"[^"]+"$/ => Interpreter TextArgument mimic(""),
#/^[-+]?\d+$/ => Interpreter NumberArgument mimic(""),
#/^\w$/ => Interpreter VariableArgument mimic(""))

fromText = method(text,
argType = typeDict find(entry,
entry key =~ text
) value
argType mimic(text)
)
)

This is better but not quite there yet. Rule 8 states "Use first-class collections. In other words, any class that contains a collection should contain no other member variables" So the typeDict collection should be contained in its own class. So lets create an ArgumentTypeMatcher kind.
ArgumentTypeMatcher = Origin mimic do( 
typeDict = {}(#/^$/ => Interpreter Argument mimic(""),
#/^"[^"]+"$/ => Interpreter TextArgument mimic(""),
#/^[-+]?\d+$/ => Interpreter NumberArgument mimic(""),
#/^\w$/ => Interpreter VariableArgument mimic(""))

match = method(text,
entryMatch = typeDict find(entry,
entry key =~ text
)
entryMatch value
)
)

Argument = Origin mimic do (
...
fromText = method(text,
argumentType = ArgumentTypeMatcher match(text)
argumentType mimic(text)
)
)

The tests still passes so lets try the next story.

Story: An assignment statement binds a value to a variable.
input:
A=12
PRINT A
output:
12

Translated to ISpec.
it("should accept an assignment statement and bind the passed in value to a variables",
Interpreter input(Interpreter TextProgram fromText("A=12"),
Interpreter TextProgram fromText("PRINT A")) should == "12\n"
)

The test fails and to make it pass we need some means to remember values for variables. A Dict feels like a natural fit for variable and their values. Assignment statements differs from all the previous statements that we have seen so far. This statement does not start with the string "PRINT" and produces no output. So we need to change the handleLine method. We will need to do a similar match operation as with the Argument type to PRINT. handleLine currently looks like this.
Interpreter = Origin mimic do (
...
handleLine = method(textProgram,
program = Interpreter Program mimic
if (textProgram printStatement?,
argument = Interpreter Argument fromText(textProgram rest)
program = Interpreter Print mimic(argument))
program execute
)
)

Lets add a ProgramTypeMatcher kind that determines if this is a PRINT statement or a variable assignment.
ProgramTypeMatcher = Origin mimic do (
typeDict = {}(#/^PRINT.+$/ => Interpreter Print mimic(""),
#/^\w=[-+]?\d+$/ => Interpreter Assignment mimic("", ""),
#/^$/ => Interpreter Program mimic("")
)
match = method(text,
entryMatch = typeDict find(entry,
entry key =~ text
)
entryMatch value
)
)

The handleLine method becomes.
handleLine = method(textProgram,
progromType = ProgramTypeMatcher match(textProgram)
program = programType fromTextProgram(textProgram)
program execute
)


From this we can see that we need a fromTextProgram method in all Program types and a new Assignment kind for assignments.
Program = Origin mimic do (
fromTextProgram = method(textProgram, self mimic)
...
)

Print = Origin mimic do (
initialize = method(argument, @argument = argument)
fromTextProgram = method(textProgram,
argument = Interpreter Argument fromText(textProgram rest)
self mimic(argument)
)
...
)

Assignment = Origin mimic do (
initialize = method(variable, value,
@variable = variable
@value = value
)
fromTextProgram = method(textProgram,
match = #/^({variable}\w)=({value}[-+]?\d+)$/ =~ textProgram asText
variable = match variable
value = match value
self mimic(variable, value)
)
)

And we need a execute method in the Assignment kind and a kind to store the variable values in, lets call it Variables.
Variables = Origin mimic do (
variableDict = Dict withDefault(0)
setValue = method(variable, value,
variableDict[variable] = value
)
getValue = method(variable,
variableDict[variable]
)
)

Assignment = Origin mimic do (
...
execute = method(
Interpreter Variables setValue(@variable, @value)
""
)
)

Finally we need to change the evaluate method in the VariableArgument kind, to get the value from the Variables kind instead.

VariableArgument = Argument mimic("") do(
evaluate = method(Interpreter Variables getValue(@value))
)

All the test passes with this code, but the ProgramTypeMatcher and ArgumentTypeMatcher are almost identical so lets merge them into one kind.
TypeMatcher = Origin mimic do (
initialize = method(typeDict, @typeDict = typeDict)
withTypeDict = method(typeDict,
Interpreter TypeMatcher mimic(typeDict)
)
match = method(text,
entryMatch = typeDict find(entry,
entry key =~ text
)
entryMatch value
)
)

And initialize it with the typeDict that we want to use.

This article is growing a bit large and if anyone is still reading I will spare you the rest of the details. Anyway, I continued with the remaining stories, you can find my final solution on github.

Conclusion
It was pretty easy to solve this problem in Ioke and follow the extreme oop rules. After a while I learned the rules and could feel when code I was writing was breaking one of them. I created far more small kinds than I would have otherwise and was able to keep the methods pretty small and focused one one task. I liked the exercise and will try it again in some other language and with another program exercise. I could probably have written more ISpec tests for certain parts of the system. Maybe I could have used the stories in the pdf as a kind of acceptance tests and then written unit tests as I tried to make the acceptance test pass.
Instead of all the code we wrote above you could use the fact that the statements we want to support are valid Ioke statements, except for print. So we can simple use the doText method on the Ioke Message kind to solve the exercise. With similar methods as in the above solution.

Interpreter = Origin mimic do (
input = method(+textPrograms,
textPrograms map(text,
text evaluate
) join
)
print = method(arg, Origin mimic)
TextProgram = Origin mimic do(
initialize = method(text, @text = text)
evaluate = method(Message doText(text))
)
)

And all the tests passes.

Tuesday, July 7, 2009

Prototype based programming in Ioke

Lets look a bit closer on programming in a prototype based language. In class based object oriented programming we have the concept of classes and objects of these classes. The objects behavior is defined in the class, and new objects are created from the class. If we want to reuse behavior in the original class but alter it in some way we can either subclass the original class or delegate calls from a new class to the original one. In contrast, in a prototype based language we create new objects from already existing objects in the system. In the new object we can redefine variables and methods or choose to leave them unaltered. If the new object does not provide a definition for a certain variable the prototype object will be searched, continuing further up the chain until the topmost object in the system is reached, in Ioke called Origin. So if we want to model an elephant in Ioke we can do so by creating a mimic of Origin, and defining the behavior and state we want to use to represent an elephant on that object.

iik> joe = Origin mimic
iik> joe color = "grey"
iik> joe legs = 4

If we then want to create another elephant we can mimic Joe and redefine the cells that differs from Joe's.

iik> charlie = joe mimic
iik> charlie legs = 3
iik> charlie color
+> "grey"

Ioke also has the concept of mixins, similar to traits in Self and Scala, which allows you to share behavior between different classes that don't share a common base object. You create new mixins by mimicing the Mixins kind (strictly speaking you can use any object as a mixin but the convention is to use a mimic of the Mixins kind). The Mixins kind does not have Base in its mimic chain, so you should not create objects (other than new mixins) from the Mixins kind.

iik> runBehavior = Mixins mimic
iik> runBehavior run = method("Run for your life" println)

If we want to use this mixin in our charlie object we add it to charlie's mixin chain with the mixin! method.

iik> charlie mixin!(runBehavior)
iik> charlie run
Run for your life
+> nil

Sokoban example
If you are used to class based object oriented programming it might feel strange to not have any objects to put your behavior in. But if you use it for a while you will notice that it is quite natural. If you want to know more about modeling in a prototype based language I can recommend Organizing Programs Without Classes, a paper about programming in the self language, and Steve Yegge's The Universal Design Pattern.

To get a fell for what a program in a prototype based language can look like I decided to try to translate a Ruby program to Ioke. I chose one of the solutions to the Sokoban Ruby Quiz. You can see the solution by Dave Burt here. The problem in the quiz is to create an implementation of the Sokoban game. It's a good exercise to try to translate the Ruby program yourself. The translation was pretty straightforward. The only problem I encountered was how to read files and user input on the console. But after I found the Ioke System class and its 'in' attribute it was pretty easy. I also decided to alter Dave's solution a bit to make it even more object oriented. I introduced the Direction and Position objects and added some convenience methods.

Considerations
When using the do method after creating a new object the name of objects created inside the do will be somewhat unintuitive (at leas to me). If we create an object A with a cell B inside it these two don't give the same kind for B:

A = Origin mimic
A B = Origin mimic
A B kind
+> "Origin A B"

A = Origin mimic do( B = Origin mimic )
A B kind
+> "Origin B"

So to be able to check if the resident on a certain tile is a crate or a person I reassigned the kind cell on these objects. After that we can refer to them with the name that we want, in this case Sokoban crate and Sokoban person.

After some thought I chose to make wall, crate and person objects in the Sokoban kind and not kinds in them self. Because these objects don't hold any state we do not need separate instances of them. So we can for example reuse the same crate object for all the crates on the level. But since the Floor tiles need to remember if there is anything on them we need separate objects for each of the floor tiles. So floor is a kind that we call mimic on but wall, crate and person are plain objects that we use directly.

Here is the complete Ioke code.

Sokoban = Origin mimic do (

Tile = Origin mimic do (
asText = method("~")
storage? = method(self kind == Sokoban Storage kind)
hasPerson? = method(false)
hasCrate? = method(false)
free? = method(false)
walkable? = method(false)
solved? = method(true) ; treat all tiles as solved unless they overried this method
kind = "Sokoban Tile"
)

CharTile = Tile mimic do (
initialize = method(chr,
@ chr = chr
)
asText = method(@ chr)
kind = "Sokoban CharTile"
)

wall = Tile mimic do (
asText = method("#")
kind = "Sokoban wall"
)

Floor = Tile mimic do (
initialize = method(resident nil,
@ resident = resident
)
asText = method(
if(@ resident,
@ resident asText,
" ")
)

clear = method(
r = @ resident
@ resident = nil
r
)

hasCrate? = method(!free? && @ resident kind?("Sokoban crate"))
hasPerson? = method(!free? && @ resident kind?("Sokoban person"))
free? = method(!(@ resident))
walkable? = method(true)

cell("<<") = method(resident,
if(@ resident, error!("Can't go there - this tile is full"))
@ resident = resident
)

kind = "Sokoban Floor"
)

Storage = Floor mimic do (
initialize = method(resident nil,
super(resident)
)

asText = method(
case(@ resident kind,
Sokoban crate kind, "*",
Sokoban person kind, "+",
"."
)
)
solved? = method(hasCrate?)
kind = "Sokoban Storage"
)

crate = Origin mimic do (
asText = method("o")
kind = "Sokoban crate"
)

person = Origin mimic do (
asText = method("@")
kind = "Sokoban person"
)

Tile createTile = method(chr nil,
case(chr,
"#", Sokoban wall,
" ", Sokoban Floor mimic,
"@", Sokoban Floor mimic(Sokoban person),
"o", Sokoban Floor mimic(Sokoban crate),
".", Sokoban Storage mimic,
"+", Sokoban Storage mimic(Sokoban person),
"*", Sokoban Storage mimic(Sokoban crate),
Sokoban CharTile mimic(chr)
)
)

Point = Origin mimic do(
initialize = method(x, y,
@ x = x
@ y = y
)
)

Direction = Point mimic(0,0) do(
kind = "Sokoban Direction"

cell("*") = method(mult,
Sokoban Position mimic(@ x * 2, @ y * 2)
)
)

Position = Point mimic(0,0) do(
cell("+") = method(dir,
Sokoban Position mimic(@ x + dir x, @ y + dir y)
)

kind = "Sokoban Position"
)

NORTH = Direction mimic(-1,0)
SOUTH = Direction mimic(1,0)
EAST = Direction mimic(0,1)
WEST = Direction mimic(0,-1)

Level = Origin mimic do(
initialize = method(levelsString,
@ grid = createLevels(levelsString)
if(!playerIndex, error!("No player found on level"))
if(solved?, error!("No challenge!"))
@ moves = 0
)

kind = "Sokoban Level"

createLevels = method(levelsString,
levelsString split("\n") map(ln,
ln split("") map(c, Sokoban Tile createTile(c) )
)
)

cell("[]") = method(position,
@ grid[ position x ][ position y ]
)

asText = method(
@ grid map(row, row join) join("\n")
)

playerIndex = method(
"Returns the position of the player"
@ grid each(rowIndex, row,
row each(colIndex, tile,
if(tile hasPerson?,
return Sokoban Position mimic(rowIndex, colIndex)
)
)
)
nil
)

solved? = method(
; a level is solved when every tile is solved
@ grid flatten all?(solved?)
)

moveCrate = method(
"Move the crate in front of the player in the given direction",
cratePos, newCratePos,
self[newCratePos] << self[cratePos] clear
)

movePlayer = method(
"Move the player to the given position",
newPlayerPos,
self[newPlayerPos] << self[playerIndex] clear
)

move = method(
"Move the player in the given direction and update the level to reflect it",
dir,
pos = playerIndex
playerTarget = pos + dir
if(self[playerTarget] walkable?,
if(self[playerTarget] hasCrate?,
crateTarget = pos + dir * 2
if(self[crateTarget] free?,
moveCrate(playerTarget, crateTarget)
movePlayer(playerTarget)
),
movePlayer(playerTarget)
)
return @ moves += 1
)
nil
)
)

; command-line interface
cli = method(levelsFile "sokoban_levels.txt",
cliHelp = "

Dave's Cheap Ruby Sokoban
Ported to Ioke by Mikael Amborn
© Dave Burt 2004

@ is you
+ is you standing on storage
# is a wall
. is empty storage
o is a crate
* is a crate on storage

Move all the crates onto storage.

to move: k
|
h -+- l
|
j
to restart the level: r
to quit: q
to show this message: ?

You can queue commands like this: hhjjlll...

"
cliHelp replaceAll(#/\t+/, " : ")
cliHelp println
"Press [enter] to begin." println
System in read

FileSystem readFully(levelsFile) split("\n\n") each(levelIndex, levelString,
level = Sokoban Level mimic(levelString)
while(!level solved?,
level println
"L:#{levelIndex+1} M:#{level moves} > " print
System in read asText split("") each(c,
case(c,
"h", level move(Sokoban WEST),
"j", level move(Sokoban SOUTH),
"k", level move(Sokoban NORTH),
"l", level move(Sokoban EAST),
"r", level = Sokoban Level mimic(levelString),
"q", "Bye!" println . System exit,
"d",
"ioke> " print
bind(
handle(fn(c, c println)),
Message doText(System in read code) println
),
"?", cliHelp println,
or("\n", "\r", "\t", " ", "."),
; System in read gives ".\n" if you just press enter so ignore . as well
nil,
"Invalid command: #{c}" println
)
)
)
"\nCongratulations - you beat level #{levelIndex + 1}!\n\n" println
)
)
)

;Sokoban cli



To be able to do the refactorings I did without breaking the implementation I also added some ISpec tests.

use("ispec")
use("sokoban_refactor")

SIMPLE_LEVEL = "#####\n#@o.#\n#####"
SOLVED_LEVEL = "#####\n# @*#\n#####"

position = method(x, y,
Sokoban Position mimic(x,y)
)
direction = method(x, y,
Sokoban Direction mimic(x,y)
)

describe(Sokoban,

describe(Sokoban wall,
it("should have the correct kind",
Sokoban wall should have kind("Sokoban wall")
)

it("should not be possible to walk on",
Sokoban wall should not be walkable
)
)

describe(Sokoban Crate,
it("should have the correct kind",
Sokoban Crate should have kind("Sokoban Crate")
)
)

describe(Sokoban Floor,
it("should have the correct kind",
Sokoban Floor should have kind("Sokoban Floor")
)
)

describe(Sokoban Floor,

it("should have the correct kind",
Sokoban Floor should have kind("Sokoban Floor")
)

describe("walkable?",
it("should be possible to walk on",
Sokoban Floor mimic should be walkable
)
)

describe("<<",
before(floor = Sokoban Floor mimic)
it("should be able to add a person to the floor tile",
floor clear
floor << Sokoban person
floor hasPerson? should == true
)

it("should be able to add a crate to the floor tile",
floor clear
floor << Sokoban crate mimic
floor hasCrate? should == true
)
)

describe("clear",
before(floor = Sokoban Floor mimic)
it("should return and remove resident",
floor clear
floor << Sokoban Crate mimic
floor clear should have kind("Sokoban Crate")
)
)

describe("free?",
before(floor = Sokoban Floor mimic)
it("should return true when there is no one on the tile",
floor clear
floor should be free
)
)

)

describe(Sokoban Storage,

it("should have the correct kind",
Sokoban Storage should have kind("Sokoban Storage")
)

describe("walkable?",
it("should be possible to walk on",
Sokoban Storage mimic should be walkable
)
)

describe("solved?",
before(storage = Sokoban Storage mimic)

it("should return false when it has no resident",
storage should not be solved
)

it("should return false when it has a person on it",
storage << Sokoban person
storage should not be solved
)

it("should return true when it has a crate on it",
storage clear
storage << Sokoban crate
storage should be solved
)
)
)

describe(Sokoban Tile,
describe("createTile",
it("should be able to create Floor tiles",
Sokoban Tile createTile(" ") should have kind("Sokoban Floor")
)
it("should be able to create Wall tiles",
Sokoban Tile createTile("#") should have kind("Sokoban wall")
)
it("should be able to create Storage tiles",
Sokoban Tile createTile(".") should have kind("Sokoban Storage")
)
it("should be able to create Floor tiles with a Person on them",
floorWithPerson = Sokoban Tile createTile("@")
floorWithPerson should have kind("Sokoban Floor")
floorWithPerson hasPerson? should be true
)
it("should be able to create Floor tiles with a Crate on them",
floorWithCrate = Sokoban Tile createTile("o")
floorWithCrate should have kind("Sokoban Floor")
floorWithCrate hasCrate? should be true
)
it("should be able to create Storage tiles with a Person on them",
floorWithPerson = Sokoban Tile createTile("+")
floorWithPerson should have kind("Sokoban Storage")
floorWithPerson hasPerson? should be true
)
it("should be able to create Storage tiles with a Crate on them",
floorWithCrate = Sokoban Tile createTile("*")
floorWithCrate should have kind("Sokoban Storage")
floorWithCrate hasCrate? should be true
)

)
)

describe(Sokoban Direction,
it("should have the correct kind",
Sokoban Direction should have kind("Sokoban Direction")
)

describe("*",
it("should multiply x and y with the given multiplyer",
dir = direction(1,2) * 2
dir x should == 2
dir y should == 4
)
)
)

describe(Sokoban Position,
it("should have the correct kind",
Sokoban Position should have kind("Sokoban Position")
)

it("should have an x and a y coordinate",
pos = position(1,2)
pos x should == 1
pos y should == 2
)

describe("+",
it("should add a direction to a position",
pos = position(1,2) + direction(1,2)
pos x should == 2
pos y should == 4
)
)
)

describe(Sokoban Level,

it("should have the correct kind",
Sokoban Level should have kind("Sokoban Level")
)

it("should be able to construct a level from a string",
Sokoban Level mimic(SIMPLE_LEVEL)
; just check that we do not get any errors
)

describe("asText",
before(level = Sokoban Level mimic(SIMPLE_LEVEL))
it("should return the level as a string",
level asText should == SIMPLE_LEVEL
)
)

describe("[]",
before(level = Sokoban Level mimic(SIMPLE_LEVEL))
it("should return the tile at the given index",
level[position(1,3)] should have kind("Sokoban Storage")
)
)

describe("playerIndex",
before(level = Sokoban Level mimic(SIMPLE_LEVEL))
it("should return the index of the player",
level playerIndex x should == 1
level playerIndex y should == 1
)
)

describe("movePlayer",
before(level = Sokoban Level mimic(SIMPLE_LEVEL))
it("should update the players position",
level movePlayer(position(1,3))
level playerIndex x should == 1
level playerIndex y should == 3
)
)

describe("moveCrate",
before(level = Sokoban Level mimic(SIMPLE_LEVEL))
it("should update the crates position",
level moveCrate(position(1,2), position(1,3))
level[position(1,3)] hasCrate? should be true
level[position(1,2)] hasCrate? should be false
)
)

describe("move",
before(level = Sokoban Level mimic(SIMPLE_LEVEL))
it("should update both the players and the crates position",
level move(Sokoban EAST)
level asText should == SOLVED_LEVEL
)
)

describe("solved?",
before(level = Sokoban Level mimic(SIMPLE_LEVEL))
it("should return false when all crates not on storage tiles",
level should not be solved
)

it("should return true when all crates are on storage tiles",
level moveCrate(position(1,2), Sokoban position(1,3))
level should be solved
)
)
)

)

Friday, June 5, 2009

Ioke macros

This is my third post about Ioke, you can also read the first and second posts.

You define macros (a la Lisp, not C) in Ioke with the macro method on DefaultBehaviour. To quote the Ioke guide "The main difference between a macro and a method in Ioke is that the arguments to a macro are not evaluated before they are sent to the macro." The arguments to the macro are accessible via the 'arguments' method on the 'call' cell. The following macro first prints the passed in message chain and then evaluates it.
iik> l = macro(code = call arguments first . code formattedCode println  . code evaluateOn(call ground))

'.' works as a terminator, kind of like newline, which can be useful when playing with iik.
'first' is a method on List that gets the first element in the list.
'formattedCode' is a method on Message that gives a representation suitable for output.
'evaluateOn' is a method on Message that evaluates the message with a given object as ground.
'ground' is a method on Message that gives the execution ground of the macro.
There is also a dmacro, for destructing macro, that lets us match the input arguments and execute different blocks depending on input. So a dmacro that can take 2 or 3 arguments can be written like this.
myUnless = dmacro(
[>condition, falseCode]
if(condition, condition, falseCode evaluateOn(call ground)),

[>condition, falseCode, trueCode]
if(condition, trueCode evaluateOn(call ground), falseCode evaluateOn(call ground)))

The '>' before the condition parameter tells Ioke to evaluate this argument.
iik> myUnless(false, 2 + 3)
+> 5

iik> myDmacro(2>1, 1/0)
+> true

Let's write an Ioke version of the Ruby 1.9 each_with_object method with an Ioke dmacro.
"Each_with_object is the same thing as inject, except you don’t have to return the object at the end of the block you pass. Since most of the time you are using inject, you have to return the hash or array as the last line in the block, using each with object will let you do the same thing, but with less code."
If we want to create a hash from an array with inject we can write something like.
iik> ["foo", "bar", "baz"] inject({}, res, elm, res[elm] = elm upper . res)
+> {"foo" => "FOO", "baz" => "BAZ", "bar" => "BAR"}

We have to return res from the code block, but with each_with_object this is done automatically for us.
[Update]
The same thing can be done with the ioke collect method for List, like this.
iik> ["foo", "bar", "baz"] collect(elm, elm => elm upper)
+> {"foo" => "FOO", "baz" => "BAZ", "bar" => "BAR"}

Let's try to implement it with an Ioke dmacro. We will add it as a method on Mixins Enumerable so that it will be available on all classes that mixins this class, such as arrays and dicts.
Mixins Enumerable each_with_object = dmacro(
[>sum, res, arg, code]
;; save the name of the arg parameter in elementName
elementName = arg name
;; do the same with the res parameter
resName = res name
;; define a new cell on call ground
call ground cell(resName) = sum
;; add resName as the return from the passed in code block
code last -> Message fromText(".") -> message(resName)

;; loop through the array we are evaluated on, accessible through self
self each(n,
;; save the current elements value in the elementName cell so that it can be used in the passed in code
call ground cell(elementName) = cell(:n)
;; run the code on this element and save the result in the resName cell
call ground cell(resName) = code evaluateOn(call ground))
;; return the value in the resName cell from the macro
call ground cell(resName))

-> is a way in Ioke to add messages to a messages chain. Let's try our new macro with the previous example. When we enter the macro the res 'parameter' will be of type Message, to be able to use the variable name contained in res we use the 'name' function on Message. This gives us the variable name in 'resName' of type Symbol. But to be able to assign a cell with the Symbol name in 'resName' we have to use the cell() function. You can try it in iik.
iik> symbolVar = :symbolName
+> :symbolName

iik> cell(symbolVar) = 42
+> 42

iik> symbolName
+> 42

Let's try our new macro with the array to hash example we saw earlier.
iik> ["foo", "bar", "baz"] each_with_object({}, res, elm, res[elm] = elm upper)
+> {"foo" => "FOO", "baz" => "BAZ", "bar" => "BAR"}

Our macro can be simplified with the use of a lexical block in which we define the res and arg parameters. Lexical blocks can be created with fn, fnx in DefaultBehavior Definitions and the createFrom method in LexicalBlock. The lexical block can then be invoked with the call method in LexicalBlock or by using regular method invocation syntax with (). From the Ioke guide:
iik> x = fn(z, z println)
iik> x call(42)
42

iik> y = fnx(z, z println)
iik> y(42)
42

The method createFrom in LexicalBlock takes two arguments, the first is a list with arguments and code and the second is the context that the lexical scope should be created in. This lets us redefine our macro like this.
Mixins Enumerable each_with_object = dmacro(
[>sum, resName, argName, code]
code last -> Message fromText(".") -> message(resName)
lexicalCode = LexicalBlock createFrom(list(resName, argName, code), call ground)
self each(n,
sum = lexicalCode call(cell(:sum), cell(:n)))
return(sum))

In this version we reuse the sum parameter as storage of the intermediate result. This also happens to be equivalent to the way inject is defined in Ioke, F30_enumerable.ik search for inject.