Antony Denyer

Tilling the land of software

about me

I am passionate about creating great software and am particularly interested in associated supporting methodologies, namely XP and TDD.

recent public projects

Status updating…

found on

contact at

email@antonydenyer.co.uk

Roman Numerals Kata in Elixir With Pattern Matching

- - posted in elixir, tdd | Comments

Roman Numerals Kata

I wanted to have a play about with pattern matching a bit more in Elixir so I thought I would see if I could do the roman numerals kata using pattern matching and no looping.

Basic tests I -> III

I started off in the usual TDD way of writing a test for “I”

1
2
3
4
5
6
7
  test "one is I" do
    assert Roman.to_roman(1) == "I"
  end

  def to_roman(number) 
    String.duplicate("I", number) 
  end

18d663

No great shakes, we can print up to “III”. So far so simple. The next step I normally take is “IV”. It’s a bit more interesting in elixir as I wanted to exercise Guard Clauses in elixir.

Pattern Matching IV and V

What you need to do is define a guard for when you want to execute the function.

1
2
3
4
5
6
7
  test "four is IV" do
    assert Roman.to_roman(4) == "IV"
  end

  def to_roman(number) when number == 4 do
    "IV"
  end

9dcb82

All we have here is a conditional guard clause to say if the number is equal to 4 then run this function. If it doesn’t match, we don’t care. It is not our responsibility to deal with, which for me is extremely interesting, we are in effect following the SRP at a function level.

One thing that caught me out was guard precedence, basically, it’s top to bottom.

Let’s move on, next is 5

1
2
3
  def to_roman(number) when number == 5 do
    "V"
  end

b427555

Boom, done.

Implementing VI

You may have noticed that we haven’t implmeneted anything interesting yet. We have some behaviour for 1-3, but that is all. 4 and 5 are just lookups. The next exciting thing to implement is 6, which is funny because 4 hasn’t added anything to help us drive out the behaviour. For 6 (VI) we need to accumulate 5 and 1. So let’s get started

1
2
3
  test "six is VI" do
    assert Roman.to_roman(6) == "VI"
  end

What I want to do is match anything greater than 5 and return “V” along with the remaining match for 1. This what I came up with:

1
2
3
  def to_roman(number) when number >= 5 do
    to_roman(number - 5, "V")
  end

The problem is we don’t have anything that will match to_roman/2, so let’s implement that.

1
2
3
  def to_roman(number, roman_accumulator) do
    roman_accumulator <> String.duplicate("I", number)
  end

155de6

And now are concatenating the two string together, the “V” and the “I”. Now, I’ve taken a bit of leap, we have implemented 7 and 8. But, I think that’s okay.

Now, our code is looking a little bit messy. Let’s go ahead and refactor it a bit.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  def to_roman(number, roman_accumulator) do
    roman_accumulator <> String.duplicate("I", number)
  end
  
  def to_roman(number) when number >= 5 do
    to_roman(number - 5, "V")
  end

  def to_roman(number) when number == 4 do
    to_roman(number - 4, "V")
  end

  def to_roman(number) do
    to_roman(number, "")
  end

c79d40

We still don’t quite have anything generic yet. Let’s implement “IX” and “X” in the same way, get some tests round them.

238510

A more generic algorithm

Now there’s still nothing going on to help us design our algorithm, let’s move onto “XIV”. There’s a bit more happening, we will need to combine “X” and “IV”. When we run our test for 14 we get a test failure:

1
2
3
4
Assertion with == failed
code:  assert Roman.to_roman(14) == "XIV"
left:  "XIIII"
right: "XIV"

e055fc

So what we need to have is a way for “X” to fall through into “IV”. To achieve this, I’m just going to add in the parameter.

1
2
3
  def to_roman(number, roman_accumulator) when number >= 4 do
    to_roman(number - 4, roman_accumulator <> "IV")
  end

Now we are starting to see some design emerging, but we have two tests failing.

1
2
3
  code:  assert Roman.to_roman(4) == "IV"
     left:  "IIII"
     right: "IV"

The reason is simple; we need to ensure that

1
2
3
  def to_roman(number, roman_accumulator) do
    roman_accumulator <> String.duplicate("I", number)
  end

doesn’t catch the case for 4. We can achieve this in two ways; I’m going to implement both as I think it will make the code clearer. We will implement a guard clause and change the order of the code.

403e18

And now our test passes. Now I think we’re getting closer to something more generic. Let’s implement “XVI” and tidy up. Again when we write our test for 16 we get

1
2
3
4
Assertion with == failed
code:  assert Roman.to_roman(16) == "XVI"
left:  "XIVII"
right: "XVI"

Now, what is going on here? It’s fairly simple when you dig into it. We get the match for 10 (X), remainder 6. We then get match a match on the 6 to “IV” which is incorrect. We need it to match “V”. So let’s go ahead and implement that

1
2
3
  def to_roman(number, roman_accumulator) when number >= 5 do
    to_roman(number - 5, roman_accumulator <> "V")
  end

da5e6a

Now I’m going to tidy up the 9 and 10 so that it follows the same pattern as 4 and 5.

78a7c4

I think we’ve got something that will cover us until the next numeral, let’s just double check with another test.

8a7f9d

What I’m going to do now is go ahead and implement the rest of the algorithm and test it using a doctest

Solution

4a463fe

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def to_roman(number, roman_accumulator) when number >= 1000 do
    to_roman(number - 1000, roman_accumulator <> "M")
  end
  
  def to_roman(number, roman_accumulator) when number >= 900 do
    to_roman(number - 900, roman_accumulator <> "CM")
  end
  
  def to_roman(number, roman_accumulator) when number >= 500 do
    to_roman(number - 500, roman_accumulator <> "D")
  end
  
  def to_roman(number, roman_accumulator) when number >= 400 do
    to_roman(number - 400, roman_accumulator <> "CD")
  end
  
  def to_roman(number, roman_accumulator) when number >= 100 do
    to_roman(number - 100, roman_accumulator <> "C")
  end

  def to_roman(number, roman_accumulator) when number >= 90 do
    to_roman(number - 90, roman_accumulator <> "XC")
  end

  def to_roman(number, roman_accumulator) when number >= 50 do
    to_roman(number - 50, roman_accumulator <> "L")
  end

  def to_roman(number, roman_accumulator) when number >= 40 do
    to_roman(number - 40, roman_accumulator <> "XL")
  end

  def to_roman(number, roman_accumulator) when number >= 10 do
    to_roman(number - 10, roman_accumulator <> "X")
  end

  def to_roman(number, roman_accumulator) when number >= 9 do
    to_roman(number - 9, roman_accumulator <> "IX")
  end

  def to_roman(number, roman_accumulator) when number >= 5 do
    to_roman(number - 5, roman_accumulator <> "V")
  end

  def to_roman(number, roman_accumulator) when number == 4 do
    roman_accumulator <> "IV"
  end

  def to_roman(number, roman_accumulator) when number <= 3 do
    roman_accumulator <> String.duplicate("I", number)
  end

  def to_roman(number) do
    to_roman(number, "")
  end

Pretty cool I think :)

Removing the String.duplicate

As a bit of a mental exercise, I thought it would be interesting to remove the String.duplicate basically you end up with a pattern match for 0,1,2 and 3. Interestingly most people don’t test 0 when doing this Kata. It becomes obvious that you need it when you don’t implement a pattern match for 0.

e75fd6

The other thing I wanted to look at was removing the unnecessary fall through for 4 down to 0.

358fade

Summary

We’ve learnt a bit about pattern matching and guard clauses in Elixir as well as introducing doctest. This sort of thing can be done in a few different ways.

My personal favourite is

1
2
3
4
5
6
7
8
9
10
11
12
13
String.duplicate("I", number)
    |> String.replace("IIIII", "V")
    |> String.replace("VV", "X")
    |> String.replace("XXXXX", "L")
    |> String.replace("LL", "C")
    |> String.replace("CCCCC", "D")
    |> String.replace("DD", "M")
    |> String.replace("IIII", "IV")
    |> String.replace("VIV", "IX")
    |> String.replace("XXXX", "XL")
    |> String.replace("LXL", "XC")
    |> String.replace("CCCC", "CD")
    |> String.replace("DCD", "CM")

It’s probably a bit terse; you have to understand whats going on. It’s a little hard to figure our but it’s kind of fun.

The Value of Functions

- - posted in aws, azure functions, faas, lambda, serverless | Comments

With the emergence of FaaS entering the mainstream, we are starting to see some real world business value in using such an architecture. Many of the benefits of using FaaS are discussed elsewhere:

  • pay as you go
  • no-ops
  • limited provisioning
  • performance scalability
  • lower running costs

But some of the benefits are more subtle. The benefit I want to highlight is this:

You can measure how much features cost to run.

While this may not seem like a winning feature, let’s think about it for a second.
You have a feature on your website called ‘price match’. What it does is matches prices against your competitor’s prices. To do this it scrapes each of your competitor’s sites to get their price of the product. If you ask the product owner how often they want the prices updated they’d probably say something like ‘as much as possible’ or ‘real time please’. In the old world, we would probably hit it as often as we could and be done with.

Now with FaaS you have some additional information to help you make decisions, this feature will cost 0.001ยข to run per competitor per product. You can then extrapolate different price points for different frequencies and make a business case for each of them. Or better yet measure the value of having it run every minute vs. every hour.

Summary

As an industry, we can genuinely begin looking at the running costs of an individual feature. This started at the product level with ‘Lean Startup’ and will, I believe, continue to a greater degree with individual features and functions.

Features can be added and removed more quickly with the costs and benefits of them being more transparent to both developers and product owners.

The Novel Developer

- - posted in developers | Comments

The trend of re-inventing the wheel is on the rise. As new developers enter the industry, there appears to be a tendency to prefer the novel over the stable. I’ve seen developers favour one library over another simply by the number of commits or by how active it is. While I understand the desire to use libraries that are ‘actively’ maintained I do not understand the desire to use a library that is constantly changing. I would expect most developers would want their code to change more quickly than the libraries they are dependent upon. It struck me; they’re not in search of solutions or a way to add value, they’re in search of novelty.

Daily Mail Developer

They’re in search of constant titillation and ‘gossip’. They need to be using something new to feel like they’re innovating. Using a different library or the latest js build tool is not innovative, at best it’s novel at worst it’s naive.

More Yak Shaving

We could be solving real world problems, business or otherwise. Instead, we’re moving from bower to grunt or gulp or whatever.

Beware the novel developer