Monday, September 28, 2015

Kata: Integer Ranges in Clojure

I just did the Integer Ranges kata in Clojure.

These are the tests using Midje:

(ns integer-ranges.core-test
(:use midje.sweet)
(:use [integer-ranges.core]))
(facts
"about integer-ranges"
(fact
"it knows which numbers an interval includes"
(includes? "[2, 5]" "{2,3,4,5}") => true
(includes? "[2, 5]" "{2,-1}") => false
(includes? "[2, 5)" "{5}") => false
(includes? "(2, 5]" "{2}") => false
(includes? "(2, 5)" "{2}") => false
(includes? "(2, 5)" "{5}") => false)
(fact
"it tells all numbers an interval includes"
(all-numbers "[2,5]") => [2 3 4 5]
(all-numbers "[1,5]") => [1 2 3 4 5]
(all-numbers "(1,5]") => [2 3 4 5]
(all-numbers "(1,5)") => [2 3 4])
(fact
"it knows when an interval contains another interval"
(contains-range? "[2,10)" "[2,5]") => true
(contains-range? "(2,10]" "[2,5]") => false
(contains-range? "[2,4]" "[2,5]") => false
(contains-range? "[2,4]" "[2,5)") => true)
(fact
"it knows an interval's end points"
(end-points "[3,8]") => [3 8])
(fact
"it knows when two intervals overlap"
(overlaps? "[2,10)" "[9,10)") => true
(overlaps? "[2,10)" "[1,2)") => false
(overlaps? "[2,10)" "[10,12)") => false
(overlaps? "[2,10]" "[10,12)") => true
(overlaps? "[2,10]" "[3,5)") => true
(overlaps? "[3,5)" "[2,10]") => true
(overlaps? "[9,10)" "[2,10)") => true
(overlaps? "[1,2)" "[2,10)") => false
(overlaps? "[1,2)" "[5,10)") => false
(overlaps? "[2,10]" "(10,20)") => false
(overlaps? "[2,10]" "[10,20)") => true
(overlaps? "[2,10)" "[10,20)") => false)
(fact
"it knows if two intervals are equal or not
(being equal means that they include the same numbers)"
(equals? "[2,10)" "[9,10)") => false
(equals? "[5,8]" "[5,8]") => true
(equals? "[5,8]" "[5,9)") => true
(equals? "[4,8]" "(3,9)") => true
(equals? "(4,8]" "[5,9)") => true))
and this is the resulting code:

(ns integer-ranges.core
(:require [clojure.string :as string]))
(defn- remove-spaces [s]
(string/replace s #" " ""))
(defn- remove-brackets [descriptor]
(apply str (drop-last (drop 1 descriptor))))
(defn- numbers-descriptors [descriptor]
(-> descriptor
remove-spaces
remove-brackets
(string/split #",")))
(defn- numbers [numbers-list-descriptor]
(map #(Integer/parseInt (str %))
(numbers-descriptors numbers-list-descriptor)))
(defn- brackets [descriptor]
(let [stripped_descriptor (string/replace descriptor #" " "")]
[(first stripped_descriptor) (last stripped_descriptor)]))
(defn- closed-open-interval [descriptor]
(let [[lower upper] (numbers descriptor)
[opening-bracket closing-bracket] (brackets descriptor)]
[(if (= opening-bracket \[) lower (inc lower))
(if (= closing-bracket \]) (inc upper) upper)]))
(defn- includes-number? [[lower upper] number]
(<= lower number (dec upper)))
(defn includes? [interval-descriptor numbers-descriptor]
(every?
#(includes-number? (closed-open-interval interval-descriptor) %)
(numbers numbers-descriptor)))
(defn all-numbers [descriptor]
(apply range (closed-open-interval descriptor)))
(defn contains-range? [descriptor other-descriptor]
(let [[lower upper] (closed-open-interval descriptor)
[other-lower other-upper] (closed-open-interval other-descriptor)]
(<= lower other-lower other-upper upper)))
(def end-points numbers)
(defn overlaps? [descriptor other-descriptor]
(let [[lower upper] (closed-open-interval descriptor)
[other-lower other-upper] (closed-open-interval other-descriptor)]
(and (< lower other-upper) (> upper other-lower))))
(defn equals? [descriptor other-descriptor]
(= (closed-open-interval descriptor)
(closed-open-interval other-descriptor)))
As usual I used a mix of TDD and REPL-driven development committing after each green and each refactoring. I also committed the REPL history.

See all the commits here if you want to follow the process.

You can find all the code on GitHub.

Thursday, September 24, 2015

Interesting Talk: "A Rubyist in Clojure-land"

I've just watch this great talk by David Chelimsky:

Kata: Password validation in Python

Yesterday I did the Password validation kata in Python.

I used TDD to write the code.

These are the tests using Pytest:

import pytest
from password_validator import PasswordValidator
@pytest.fixture
def validator():
return PasswordValidator(8)
def test_a_strong_password(validator):
assert validator.is_strong_password("#Ab3cccc") is True
def test_that_only_passwords_with_the_minimum_length_are_strong(validator):
assert validator.is_strong_password("#Ab3ccc") is False
def test_that_only_passwords_including_numbers_are_strong(validator):
assert validator.is_strong_password("#Abccccc") is False
def test_that_only_passwords_including_upper_case_letters_are_strong(validator):
assert validator.is_strong_password("#ab3cccc") is False
def test_that_only_passwords_including_lower_case_letters_are_strong(validator):
assert validator.is_strong_password("#AB3CCCC") is False
def test_that_only_passwords_including_special_characters_are_strong(validator):
assert validator.is_strong_password("cAb3cccc") is False
and this is the resulting code:

import re
class PasswordValidator(object):
def __init__(self, minimum_length):
self.minimum_length = minimum_length
def is_strong_password(self, password):
includes_special_characters = self._includes_any(
self.REQUIRED_SPECIAL_CHARACTERS, password
)
includes_lower_case_letters = self._includes_any(
self.LOWER_CASE_LETTERS, password
)
includes_upper_case_letters = self._includes_any(
self.UPPER_CASE_LETTERS, password
)
includes_numbers = self._includes_any(self.NUMBERS, password)
has_minimum_length = len(password) >= self.minimum_length
return \
has_minimum_length and \
includes_numbers and \
includes_upper_case_letters and \
includes_lower_case_letters and \
includes_special_characters
@staticmethod
def _includes_any(pattern, password):
return re.search(pattern, password) is not None
REQUIRED_SPECIAL_CHARACTERS = '[%#]'
UPPER_CASE_LETTERS = '[A-Z]'
LOWER_CASE_LETTERS = '[a-z]'
NUMBERS = '[0-9]'


If you want to follow the process step by step, have a look at the commits.

You can find all the code in GitHub.

Wednesday, September 23, 2015

Kata: Word wrap in Clojure

Last night I did the Word Wrap kata in Clojure.

It was proposed in the last Barcelona Software Craftsmanship coding dojo but I couldn't attend, so I did it at home.

These are the tests using Midje:

(ns word-wrap.core-test
(:use midje.sweet)
(:use [word-wrap.core]))
(facts
"about wrapping words"
(fact
"a text that fits in the given columns number are not wrapped"
(wrap "koko koko" 9) => "koko koko"
(wrap "ko" 9) => "ko"
(wrap "" 9) => "")
(fact
"a text without spaces that doesn't fit in the given columns number are wrapped"
(wrap "kokokoko" 4) => "koko\nkoko")
(fact
"a text with spaces that doesn't fit in the given columns number are wrapped
at the space that is closest to the maximum column"
(wrap "koko koko" 7) => "koko\nkoko"
(wrap "koko koko koko" 12) => "koko koko\nkoko"
(wrap "koko koko koko koko koko koko" 12) => "koko koko\nkoko koko\nkoko koko"
(wrap
"This koko should be easy unless there are hidden, or not so hidden, obstacles. Let's start!"
12) => "This koko\nshould be\neasy unless\nthere are\nhidden, or\nnot so\nhidden,\nobstacles.\nLet's start!")
(fact
"a text already splitted in lines gets each of its lines re-wrapped"
(wrap
"kokokoko\nkaka koko\nkoko koko"
6) => "kokoko\nko\nkaka\nkoko\nkoko\nkoko"))
and this is the resulting code:

(ns word-wrap.core
(:require [clojure.string :as string]))
(def ^:private to-trimmed-string
(comp string/trim (partial apply str)))
(def ^:private rest-of-line
(comp to-trimmed-string (partial drop)))
(defn- wrap-line-at [index line]
(str (to-trimmed-string (take index line)) \newline))
(defn- index-of-last-fitting-space [max-columns line]
(.lastIndexOf (take max-columns line) \space))
(def ^:private valid-index? pos?)
(defn- compute-wrapping-index [line max-columns]
(let [index (index-of-last-fitting-space max-columns line)]
(if (valid-index? index)
index
max-columns)))
(defn- fits? [line max-columns]
(<= (count line) max-columns))
(defn- line->wrapped-lines [wrapped-lines line max-columns]
(if (fits? line max-columns)
(conj wrapped-lines line)
(let [index (compute-wrapping-index line max-columns)]
(recur (conj wrapped-lines (wrap-line-at index line))
(rest-of-line index line)
max-columns))))
(defn- wrap-line [line max-columns]
(apply str (line->wrapped-lines [] line max-columns)))
(defn- extract-lines [text]
(string/split text #"\n"))
(def ^:private join-lines (partial string/join \newline))
(defn wrap [text max-columns]
(->> text
extract-lines
(map #(wrap-line % max-columns))
join-lines))
view raw word_wrap.clj hosted with ❤ by GitHub
As usual I used a mix of TDD and REPL-driven development committing after each green and each refactoring. I also committed the REPL history. See all the commits here to follow the process.

Once I got to a tail recursive solution, I tried to make it more readable by extracting some explanatory helpers and working in the naming of bindings, functions and function arguments.

You can find all the code in GitHub.

Sunday, September 20, 2015

Kata: Yatzi refactoring kata in Ruby

Last night I did the Yatzy refactoring kata in Ruby.

I mainly used it to practice with Enumerable functions.

This is the original code:

class Yatzy
def self.chance(d1, d2, d3, d4, d5)
total = 0
total += d1
total += d2
total += d3
total += d4
total += d5
return total
end
def self.yatzy(dice)
counts = [0]*(dice.length+1)
for die in dice do
counts[die-1] += 1
end
for i in 0..counts.size do
if counts[i] == 5
return 50
end
end
return 0
end
def self.ones( d1, d2, d3, d4, d5)
sum = 0
if (d1 == 1)
sum += 1
end
if (d2 == 1)
sum += 1
end
if (d3 == 1)
sum += 1
end
if (d4 == 1)
sum += 1
end
if (d5 == 1)
sum += 1
end
sum
end
def self.twos( d1, d2, d3, d4, d5)
sum = 0
if (d1 == 2)
sum += 2
end
if (d2 == 2)
sum += 2
end
if (d3 == 2)
sum += 2
end
if (d4 == 2)
sum += 2
end
if (d5 == 2)
sum += 2
end
return sum
end
def self.threes( d1, d2, d3, d4, d5)
s = 0
if (d1 == 3)
s += 3
end
if (d2 == 3)
s += 3
end
if (d3 == 3)
s += 3
end
if (d4 == 3)
s += 3
end
if (d5 == 3)
s += 3
end
return s
end
def initialize(d1, d2, d3, d4, _5)
@dice = [0]*5
@dice[0] = d1
@dice[1] = d2
@dice[2] = d3
@dice[3] = d4
@dice[4] = _5
end
def fours
sum = 0
for at in Array 0..4
if (@dice[at] == 4)
sum += 4
end
end
return sum
end
def fives()
s = 0
i = 0
for i in (Range.new(0, @dice.size))
if (@dice[i] == 5)
s = s + 5
end
end
s
end
def sixes
sum = 0
for at in 0..@dice.length
if (@dice[at] == 6)
sum = sum + 6
end
end
return sum
end
def self.score_pair( d1, d2, d3, d4, d5)
counts = [0]*6
counts[d1-1] += 1
counts[d2-1] += 1
counts[d3-1] += 1
counts[d4-1] += 1
counts[d5-1] += 1
at = 0
(0...6).each do |at|
if (counts[6-at-1] >= 2)
return (6-at)*2
end
end
return 0
end
def self.two_pair( d1, d2, d3, d4, d5)
counts = [0]*6
counts[d1-1] += 1
counts[d2-1] += 1
counts[d3-1] += 1
counts[d4-1] += 1
counts[d5-1] += 1
n = 0
score = 0
for i in Array 0..5
if (counts[6-i-1] >= 2)
n = n+1
score += (6-i)
end
end
if (n == 2)
return score * 2
else
return 0
end
end
def self.four_of_a_kind( _1, _2, d3, d4, d5)
tallies = [0]*6
tallies[_1-1] += 1
tallies[_2-1] += 1
tallies[d3-1] += 1
tallies[d4-1] += 1
tallies[d5-1] += 1
for i in (0..6)
if (tallies[i] >= 4)
return (i+1) * 4
end
end
return 0
end
def self.three_of_a_kind( d1, d2, d3, d4, d5)
t = [0]*6
t[d1-1] += 1
t[d2-1] += 1
t[d3-1] += 1
t[d4-1] += 1
t[d5-1] += 1
for i in [0,1,2,3,4,5]
if (t[i] >= 3)
return (i+1) * 3
end
end
0
end
def self.smallStraight( d1, d2, d3, d4, d5)
tallies = [0]*6
tallies[d1-1] += 1
tallies[d2-1] += 1
tallies[d3-1] += 1
tallies[d4-1] += 1
tallies[d5-1] += 1
(tallies[0] == 1 and
tallies[1] == 1 and
tallies[2] == 1 and
tallies[3] == 1 and
tallies[4] == 1) ? 15 : 0
end
def self.largeStraight( d1, d2, d3, d4, d5)
tallies = [0]*6
tallies[d1-1] += 1
tallies[d2-1] += 1
tallies[d3-1] += 1
tallies[d4-1] += 1
tallies[d5-1] += 1
if (tallies[1] == 1 and tallies[2] == 1 and tallies[3] == 1 and tallies[4] == 1 and tallies[5] == 1)
return 20
end
return 0
end
def self.fullHouse( d1, d2, d3, d4, d5)
tallies = []
_2 = false
i = 0
_2_at = 0
_3 = false
_3_at = 0
tallies = [0]*6
tallies[d1-1] += 1
tallies[d2-1] += 1
tallies[d3-1] += 1
tallies[d4-1] += 1
tallies[d5-1] += 1
for i in Array 0..5
if (tallies[i] == 2)
_2 = true
_2_at = i+1
end
end
for i in Array 0..5
if (tallies[i] == 3)
_3 = true
_3_at = i+1
end
end
if (_2 and _3)
return _2_at * 2 + _3_at * 3
else
return 0
end
end
end
and this is the refactored one:

class Yatzy
def self.chance *dies
dies.reduce(:+)
end
def self.yatzy *dies
return 50 if all_equal?(dies)
return 0
end
def self.ones *dies
compute_score(dies, 1)
end
def self.twos *dies
compute_score(dies, 2)
end
def self.threes *dies
compute_score(dies, 3)
end
def self.fours *dies
compute_score(dies, 4)
end
def self.fives *dies
compute_score(dies, 5)
end
def self.sixes *dies
compute_score(dies, 6)
end
def self.one_pair *dies
compute_group_of_a_kind_score(dies, 2)
end
def self.two_pairs *dies
pairs = extract_groups_with_equal_or_larger_size(dies, 2)
pairs.keys.reduce(:+) * 2
end
def self.four_of_a_kind *dies
compute_group_of_a_kind_score(dies, 4)
end
def self.three_of_a_kind *dies
compute_group_of_a_kind_score(dies, 3)
end
def self.small_straight *dies
return 15 if small_straight?(dies)
return 0
end
def self.large_straight *dies
return 20 if large_straight?(dies)
return 0
end
def self.full_house *dies
return 0 unless full_house?(dies)
compute_full_house_score(dies)
end
private
def self.compute_score dies, die_value
dies_with_value = dies.select {|die| die == die_value}
die_value * dies_with_value.size
end
def self.compute_frequencies dies
dies.inject({}) do |frequencies_so_far, die|
if frequencies_so_far.include?(die)
frequencies_so_far[die] += 1
else
frequencies_so_far.merge!({die => 1})
end
frequencies_so_far
end
end
def self.extract_groups_with_equal_or_larger_size dies, size
extract_groups(
dies,
lambda { |frequency| frequency >= size }
)
end
def self.compute_group_of_a_kind_score dies, group_size
group = extract_groups_with_equal_or_larger_size(dies, group_size)
compute_group_score(group, group_size)
end
def self.compute_group_score group, group_size
(group.keys.max || 0) * group_size
end
def self.small_straight? dies
frequencies = compute_frequencies(dies)
frequencies.all? do |die, frequency|
frequency == 1 && die != 6
end
end
def self.large_straight? dies
frequencies = compute_frequencies(dies)
frequencies.all? do |die, frequency|
frequency == 1 && die != 1
end
end
def self.all_equal? dies
dies.uniq.size == 1
end
def self.compute_full_house_score dies
pairs_score = compute_full_house_group(dies, 2)
triplets_score = compute_full_house_group(dies, 3)
pairs_score + triplets_score
end
def self.full_house? dies
triplets = extract_groups_with_equal_size(dies, 3)
pairs = extract_groups_with_equal_size(dies, 2)
only_one_pair = pairs.size == 1
only_one_triplet = triplets.size == 1
only_one_pair && only_one_triplet
end
def self.extract_groups_with_equal_size dies, size
extract_groups(
dies,
lambda { |frequency| frequency == size }
)
end
def self.compute_full_house_group dies, group_size
group = extract_groups_with_equal_size(dies, group_size)
compute_group_score(group, group_size)
end
def self.extract_groups dies, predicate
compute_frequencies(dies).select do |_, frequency|
predicate.call(frequency)
end
end
end
I committed after every small refactoring (the commits step by step).

You can find the code in this repository in GitHub.

Friday, September 11, 2015

Last Pet Project: Glowworm Swarm Optimization in Clojure

Lately I've been working bit by bit in an implementation of the Glowworm Swarm Optimization algorithm (GSO) in Clojure.

Some time ago I implemented the GSO algorithm in C++ to practice TDD.

I decided to implement it it again in Clojure to practice with something larger that Exercism exercises or katas an yet small and familiar enough to finish it in spare bits of time.

This is the code of the resulting Clojure GSO on GitHub.

This GSO Clojure version is much shorter than its C++ version.

Aside from practicing Clojure, I've learned many other things while doing it.

I'll try to enumerate them here:
  • Mixing Java and Clojure code in order to use a Sean Luke's Mersenne Twister Java implementation.
  • I learned more about Midje's checkers.
  • dire library.
  • Decomposing a bigger Clojure application in several name spaces according to roles and responsibilities.
  • Using maps to model the data.
  • Struggling to make the code more readable and keeping functions at the same level of abstraction.
  • Using only higher-order functions to compose the algorithm (I set myself the constraint to use no global configuration maps).
  • Taking advantage of those higher-order functions to test the different parts of the algorithm in isolation.
  • Separating the code that builds the graph of functions that compose the algorithm from the algorithm itself.
  • Where would I apply a library like component instead of relying only n higher-order functions.
  • Many other little Clojure things.
But overall it's been great fun.

I'd like to thank Álvaro García, Natxo Cabré and Francesc Guillén from the Clojure Developers Barcelona meetup for their feedback and Brian Jiménez for rubbing elbows with me in the first C++ version.

Wednesday, September 9, 2015

Kata: "Sieve of Eratosthenes" test-driven and explained step by step

Yesterday we practiced doing the Sieve of Eratosthenes kata at a Barcelona Software Craftsmanship event.

My partner Fernando Mora and I used TDD in Scala to write the code.

Today I did it once again in Clojure.

I'd like to explain here how I did it step by step in order to share it with the Barcelona Software Craftsmanship members.

I started by writing this test:

(ns eratosthenes-sieve.core-test
(:use midje.sweet)
(:use [eratosthenes-sieve.core]))
(facts
"about Eratosthenes sieve"
(fact
"it returns all the primes up to a given number"
(primes-up-to 2) => [2]))
which I quickly got to green by just hard-coding the response:

(ns eratosthenes-sieve.core)
(defn primes-up-to [n]
[2])
In this first test I used wishful thinking to get the function and signature I wished.

Then I wrote the following test:

(ns eratosthenes-sieve.core-test
(:use midje.sweet)
(:use [eratosthenes-sieve.core]))
(facts
"about Eratosthenes sieve"
(fact
"it returns all the primes up to a given number"
(primes-up-to 2) => [2]
(primes-up-to 3) => [2 3]))
which drove me to generalize the code substituting the hard-coded list by a code that generated a list that was valid for both the first and the second test:

(ns eratosthenes-sieve.core)
(defn primes-up-to [n]
(range 2 (inc n)))
The next test was the first one that drove me to start eliminating multiples of a number, in this case the multiples of 2:

(ns eratosthenes-sieve.core-test
(:use midje.sweet)
(:use [eratosthenes-sieve.core]))
(facts
"about Eratosthenes sieve"
(fact
"it returns all the primes up to a given number"
(primes-up-to 2) => [2]
(primes-up-to 3) => [2 3]
(primes-up-to 5) => [2 3 5]))
I made it quickly pass by using filter to only keep those that are not multiples of 2 and 2 itself:

(ns eratosthenes-sieve.core)
(defn primes-up-to [n]
(filter #(or (not= 0 (mod % 2)) (= % 2))
(range 2 (inc n))))
Alternatively, I could have taken a smaller step by just eliminating 4, then go on triangulating to also eliminate 6 and finally refactor out the duplication by eliminating all the multiples of 2 that are different from 2.

Since the implementation was obvious and I could rely on filter, I decided not to follow that path and took a larger step.

I've noticed that my TDD baby steps tend to be larger in functional languages. I think the reason is, on one hand, the REPL which complements TDD by providing a faster feedback loop for triangulation and trying things out and, on the other hand, the power of sequence functions in those languages (in the case of this kata the Scala and Clojure ones).

Once the test was passing I started to refactor that ugly one-liner and got to this more readable version:

(ns eratosthenes-sieve.core)
(defn- integers-up-to [n]
(range 2 (inc n)))
(defn- multiple-of? [n p]
(and (zero? (mod n p)) (not= n p)))
(defn primes-up-to [n]
(remove #(multiple-of? % 2) (integers-up-to n)))
in which I extracted two helpers and used remove instead of filter to better express the idea of sieving.

My next goal was to write a test to drive me to generalize the code a bit more by eliminating the hard-coded number 2 in line 10 of the code.

To do it I just needed a test that forced the code to also eliminate just the multiples of 3:

(ns eratosthenes-sieve.core-test
(:use midje.sweet)
(:use [eratosthenes-sieve.core]))
(facts
"about Eratosthenes sieve"
(fact
"it returns all the primes up to a given number"
(primes-up-to 2) => [2]
(primes-up-to 3) => [2 3]
(primes-up-to 5) => [2 3 5]
(primes-up-to 11) => [2 3 5 7 11]))
Again I could have triangulated to first eliminate 9 and then 12 before refactoring but there was an easier way at this point: to introduce recursion.

But before doing that, I quickly went to green by calling the sieve function twice to eliminate the multiples of 2 and 3:

(ns eratosthenes-sieve.core)
(defn- integers-up-to [n]
(range 2 (inc n)))
(defn- multiple-of? [n p]
(and (zero? (mod n p)) (not= n p)))
(defn- sieve [numbers prime]
(remove #(multiple-of? % prime) numbers))
(defn primes-up-to [n]
(sieve (sieve (integers-up-to n) 2) 3))
With this tiny step I both highlighted the recursive pattern and provided a safe place from which to start using refactoring to introduce recursion.

In this case I think that having tried to directly introduce recursion to make the test pass would have been too large a step to take, so I played safe.

Once in green again I safely refactored the code to introduce recursion:

(ns eratosthenes-sieve.core)
(defn- integers-up-to [n]
(range 2 (inc n)))
(defn- multiple-of? [n p]
(and (zero? (mod n p)) (not= n p)))
(defn- sieve [numbers prime]
(let [sieved (remove #(multiple-of? % prime) numbers)
next-prime (first (drop-while #(<= % prime) sieved))]
(if (nil? next-prime)
sieved
(recur sieved next-prime))))
(defn primes-up-to [n]
(sieve (integers-up-to n) 2))
Notice that this version of the code is the first one that solves the kata.

From this point on I just refactored the code trying to make it a bit more readable until I got to this version:

(ns eratosthenes-sieve.core)
(defn- integers-up-to [n]
(range 2 (inc n)))
(defn- multiple-of? [n p]
(and (zero? (mod n p)) (not= n p)))
(defn- next-prime [prime numbers]
(first (drop-while #(<= % prime) numbers)))
(defn- sieve-multiples-of [prime numbers]
(remove #(multiple-of? % prime) numbers))
(defn- sieve [numbers]
(loop [primes numbers prime 2]
(let [sieved (sieve-multiples-of prime primes)]
(if-let [prime (next-prime prime sieved)]
(recur sieved prime)
primes))))
(defn primes-up-to [n]
(sieve (integers-up-to n)))
Finally, I wrote a more thorough test that made the stepping-stone tests that helped me drive the solution redundant, so I deleted them:

(ns eratosthenes-sieve.core-test
(:use midje.sweet)
(:use [eratosthenes-sieve.core]))
(facts
"about Eratosthenes sieve"
(fact
"it returns all the primes up to a given number"
(primes-up-to 100) => [2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97]))
If you want to have a closer look at the process I followed, please check the commits list where I've also included the REPL history. You can also see all the code in this GitHub repository.

That's all.

I'd like to thank Barcelona Software Craftsmanship members for practicing together every two weeks, especially Álvaro García for facilitating this last kata, and eBay España for kindly having us yesterday (and on many previous events) in their Barcelona office.