At the bottom of the article "How do you become better at programming?" is a mention of the robot kata. In this article, we will look at how to solve it using a functional programming approach.

The Robot Kata

Here is the problem statement:

  • Imagine a robot that is at position (0, 0) and facing North.
  • It can take 3 commands: Move forward, turn left or turn right.
  • Turning left or right changes the orientation of the robot by 90 degrees without changing the position. For example, turn left will make the robot remain at (0, 0) but it will face West now
  • Moving forward will move the robot 1 step in the direction faced, so if it is at (0, 0) and facing West, after moving forwards it will be at (-1, 0) and still facing West

Given a sequence of commands (eg: FLFFLFFFRRRF) then calculate the final position and orientation of the robot.

In this article, we will see how to solve this kata using a functional programming style.

Storing the data

First, we need to decide how we want to represent the data. The robot has 3 state variables:

  • The current x coordinate
  • The current y coordinate
  • The direction it is facing: E, S, W, N

We have multiple options to represent this data. We could create a class, use a dataclass, use a namedtuple, or just a regular tuple.

Since this is a simple application, we will just go with a regular tuple

To make the data clearer, let us use Python's type hinting feature to create some data types.

  • Direction can be one of North, South, East or West
  • Position is a tuple containing the x and y coordinates
  • The overall RobotState is a tuple containing the Position and Direction
from typing import Tuple, Literal

Direction = Literal["North", "South", "East", "West"]
Position = Tuple[int, int]
RobotState = Tuple[Position, Direction]

(If you are using Python 3.10+ then you can replace Tuple with tuple.)

Now, we write a function to create the tuple. Given the position and direction it will just make a tuple out of it

def make_robot(position: Position, direction: Direction) -> RobotState:
    return (position, direction)

Moving the robot

Now that we have a way to represent the robot state, we can implement the three functions that we need. We will use structural pattern matching feature here.

def move(robot: RobotState) -> RobotState:
    (x, y), dir = robot
    match dir:
        case "North":
            return make_robot((x, y + 1), dir)
        case "South":
            return make_robot((x, y - 1), dir)
        case "East":
            return make_robot((x + 1, y), dir)
        case "West":
            return make_robot((x - 1, y), dir)

def left(robot: RobotState) -> RobotState:
    pos, dir = robot
    match dir:
        case "North":
            return make_robot(pos, "West")
        case "South":
            return make_robot(pos, "East")
        case "East":
            return make_robot(pos, "North")
        case "West":
            return make_robot(pos, "South")

def right(robot: RobotState) -> RobotState:
    pos, dir = robot
    match dir:
        case "North":
            return make_robot(pos, "East")
        case "South":
            return make_robot(pos, "West")
        case "East":
            return make_robot(pos, "South")
        case "West":
            return make_robot(pos, "North")

Each of these functions take in one RobotState as input and return a new RobotState as the output.

One interesting thing to note here is how we use the iterable unpacking feature with the tuple

(x, y), dir = robot

If you have never used type hints, iterable unpacking or structural pattern matching, then you should definitely take a look at them. They can really make your python code much easier to read and maintain. We will be doing articles on all those topics shortly.

Composing the functions

Once we have the basic functions written, we need to be able to compose them together so that we can give a sequence of commands.

Here is a compose function like the one we wrote previously, but can take any number of functions for composition

def compose(*funcs):
    first, second, *rest = funcs
    if rest:
        second = compose(second, *rest)
    return lambda *args, **kwargs: second(first(*args, **kwargs))

With that we can now write a function to execute a sequence of commands

def run_commands(commands: str):
    command_mapping = {"L": left, "R": right, "F": move}
    functions = [command_mapping[command] for command in commands]
    return compose(*functions)

and we can run a sequence

sequence = run_commands("FLFFLFFFRRRF")
start = make_robot((0, 0), "North")
end = sequence(start)
print(end) # Output is ((-1, -2), "East")

Summary

In this article, we saw how to implement the robot kata using a functional programming style in python. We create the basic functions move, left and right and then we can use compose to combine them in any order and calculate any sequence of movement.

In order for compose to work, the output of one function must match the input of the next function. What happens if this is not the case? That is the topic of the next article.

Did you like this article?

If you liked this article, consider subscribing to this site. Subscribing is free.

Why subscribe? Here are three reasons:

  1. You will get every new article as an email in your inbox, so you never miss an article
  2. You will be able to comment on all the posts, ask questions, etc
  3. Once in a while, I will be posting conference talk slides, longer form articles (such as this one), and other content as subscriber-only