Bread without commercial yeast – Sour-dough

At the time of writing, an extraordinary number of people are in some sort of lockdown, and trying to go the shops less often, and when they do, certain items are in short supply. One of those items is commercial yeast.

If you still have access to flour (preferably strong flour), salt and water, you are still in business.

The bubble structure of sourdough is often more varied
The bubble structure of sourdough is often more varied. This loaf has cheddar cheese within it too.


Sourdough is the oldest form of leavened bread – it’s basically using the natural yeasts (and bacteria) in our environment instead of commercial yeast. It is in some ways, a bit more tricky than normal bread making and in some ways more straightforward. It’s very in vogue at the moment, and there’s a wealth of resources out there, but I thought I’d summarise what had worked for me, and refer you to some of those if this is something that interests you because the sheer volume out there can be overwhelming.

Starter or Mother Culture

The heart of sour-dough is making a starter, or a culture from which you can always get yeast. To begin with you will need to provide food for the natural yeasts found in the air, or to get started more quickly, some other organic matter.

Try to find a jar with a reasonably wide mouth. A kilner jar works well for me, but if you use one, remove the rubber gasket so you can close the jar but it’s not 100% airtight. You can get all sorts of culture jars (and indeed the whole starters) on-line if you want.

I used this this approach with some green grapes to begin my starter. But you can use live yoghurt, or quite a variety of things to provide some initial yeasts. Keep your starter nice and warm if you can, but not hot when you are growing and feeding it. You probably need to feed it for a week to get it really going and even then it might be quite weak until it’s a couple of weeks old.

You need to feed your starter periodically. To do this, discard (or use) a little of the starter, and then mix 100 g strong flour and 100 ml of warm water (I use a fork in a measuring jug) and add this to your starter culture. The culture will rise – often dramatically – when fed and fall as the flour is consumed and the growth of yeast slows. How often you need to do this depends on how often you use the culture. If you are getting it going, do this once per day. If, once you have the starter going you don’t have time to do this often, put the culture in the fridge when not in use. It won’t die but will slow its replication. You can feed it once a week or so in this way.

If you get a brown liquid layer on the top of the culture after a few days of neglect, don’t worry. Some people stir this back in. I just discard it and feed. If for whatever reason your starter really fails to take off or goes bad, you can of course start again.

If you plan to bake a sour-dough loaf it’s a good idea to feed your culture some hours in advance. It will bubble and rise as the yeast consumes the flour. You ideally want to use the culture while it’s still rising (because the flour isn’t all spent).

Making bread, first proof

There are as many sourdough recipes for loaves as for bread with commercial yeast. You’ll find it’s much more dependent on your environment than commercial yeast but this makes it quite interesting. Like a lot of bread making, it often follows a pattern of letting the dough rise twice. The difference is you need to allow more time. This can be a nuisance, but it’s also, for me, what makes sourdough fit in with a busy schedule more than regular bread, because you can do one of these stages in the fridge.

I usually use a recipe somewhere between this one and this one.

  • 375 g/ 13 oz strong white flour
  • 250 g/ 9 oz sourdough starter
  • 10 g (2 tsp) salt
  • 10 g (2 tsp) brown sugar
  • 130-175 ml / 4-6 fl oz tepid water
  • olive oil, for kneading

Salt is an important component of making bread, for structure as well as flavour. I sometimes usejust a smidgen more than usual as above (you can use 7 g if you find this too much). Brown sugar is optional but can help the dough along.

Add the water gradually, the dough can go from too dry to awkwardly wet in short order. It’s easier to work with drier dough, but I’d recommend having it just a little wet to help the next stages. Remember the starter is already adding a lot of liquid content (and flour).

Sourdough normally takes a bit more time to knead and stretch. I have large hands and sometimes find the proposed times in recipes are too long. Take the guesswork out by doing the window test – cut off a piece of dough and stretch it to the light. If it can be made translucent without it quickly breaking then the structure is elastic enough and you are ready to let it rise.

Put the dough in a bowl (the one you used to mix it is fine), cover with a wet tea towel, and leave to rise. It will tend to spread more than rise vertically than regular bread and be slower. It’ll probably take three hours or so, but you will find your own routine with this.

Leave the dough under a wet tea towel to rise.
Leave the sour-dough to rise under a wet tea towel, it will tend to spread out more and rise less vertically than dough with commercial yeast.

Second rise

Now take out your dough and gently shape it. Knock out some of the air but don’t be too rough. If you fancy trying to incorporate other ingredients, like bacon, or cheese or sundried tomatoes, you can do it now. Don’t add too much at first if you are experimenting. The loaf pictured here had some cubes of cheddar, a little less than 1 cm a side added at this stage. If you have a banneton – or proving basket, liberally sprinkle it with flour, but you can make do with a bowl and towel. See this video for details, which is also a really good reference anyway.

Try your best to ensure you have the dough back into a ball, or at least seal and seam as best you can.

If you have sesame seeds or poppy seeds you’d like to add, just wet your hand, wet the dough with them and sprinkling them on top. Grated cheese also works well, sparingly.

Gently knock back the dough to remove excess air, then reshape, ready to go in a floured bowl.

Place your dough top down into your bowl. You can cover this (I use a clean bin bag that I will next use in my pedal bin after I finish using it for this). This second rise can take quite a while. Maybe up to five hours. But for me, this is the best part of sourdough – you can place this all in the fridge. The rise will continue, but very slowly. In the fridge give it 9 – 12 hours or more, overnight it best.

So you can start your dough around 8 pm, let it rise for three hours, shape and put it in the fridge, and bake it the late morning. Again, you will find your own routine.

The dough is ready when you press it slightly and the indentation remains or bounces back only very slowly.

In the Oven

Carefully extract your dough from your bowl or banneton, it may be quite sticky, especially if you didn’t flour the banneton enough.

Make a few slashes in the top of the dough with a very sharp thin knife. These help regulate the rise.

Some people like to bake the bread in a Dutch Oven – a cast iron pot. With an initial bake with the lid on, and then a while with the lid off. I don’t possess such a thing. You can use a baking tray, but I tend to use my invaluable pizza stone. Again, work out what works for you.

I preheat the oven to 230C or 210C fan. You need a good hot start so make sure it is up to heat. You can add a deep tray with cold water in the bottom shelf of your oven to help get a nice deep crust. I bake for 25 minutes at that temperature and then drop the oven temperature by 20 degrees for the last 10 minutes. Again, you will find your own sweet spot depending on your oven.

A finished sesame sourdough loaf
A finished sesame and cheese sourdough loaf

Share and share a like

Once you get your starter culture going, it’s easy to give some to someone else. Just pour some into a clean jar, and feed. Feed the rest of your original culture and you can now easily give half away.


Battleships Server / Client for Education

I’ve been teaching a first year introductory module in Python programming for Engineering at Ulster University for a few years now. As part of the later labs I have let the students build a battleships game using Object Oriented Programming – with “Fleet” objects containing a list of “Ships” and so on where they could play on one computer against themselves. But I had often wanted to see if I could get as far as an on-line server application based on similar principles that the students could use. This would teach them client server fundamentals but also let them have fun playing against each other.

Last year I finally attained that goal. I built a very simple web-server using Python Django to manage players, games, and ships. I wanted to use Python partly because it’s become my language of choice for web applications, but also because I wanted the code to be as accessible and understandable to the students as possible. I have initially placed very little emphasis on the User Interface of the server – this is partly deliberate because I wanted the students to have a strong incentive to develop this in the clients. I did however build a very simple admin view to let me see the games in progress. Additionally Django provides a very easy way to build admin views to manipulate objects. I have enabled this to let admins (well me) tweak data if I need to.

The Server

The server provides a very simple interface – an API – to let the students interact with the game. They can do this initially very directly using a web browser. They can simply type in specific web addresses to take certain actions. For instance

will, for a battleships server installed on (not a real server), list all the current active games. You’ll note the api/1.0/ part of the URL. I did this so that in future years I could change the API and add new version numbers. That’s partly the focus of a later section of this blog.

The output of all the API calls is actually encoded in JSON. This is still fairly human readable, and indeed some web browsers, like Firefox, will render this in a way that is easy to explore. Again, this makes it possible for the students to play the game with nothing more than a web browser and a certain amount of patience. This is good for exploration, but certainly not the easiest way to play. Here’s some sample output from my real games server with my students (poor Brian).

Sample games index JSON output
Sample games index JSON output – this is designed to be read by machines (a client program) but it’s still clear enough for humans so students can read it directly.

The code for the server and documentation about the API is all publicly available on my GitHub repository. You’ll also find some basic installation instructions in case you’d like to run your own game server.

I did want to build some very basic views into the server, I built a more human version of the games list, and I also built this view which was designed only for admins – obviously in the hands of normal players it would make the game a bit pointless.

Game overview for admins
Game overview for admins only.

This allowed me to see a little of what was actually going on in the games my students were playing, as well as showing the current state of ships and who owns what, it also showed a bit more info – here for another game than that above:

Game history and ship list
Game history and ship list

As you can see this shows some of the history of the game so far – this information is available to clients in the API, and likewise the list of surviving ships are shown. The API call for the ships still surviving only shows the ships for the player making the call, with a secret (password) that was generated when the player was created.

You may notice that the server automatically names ships with names taken from the Culture novels from Iain M. Banks.

The Client(s)

In this way the students are using their web browsers as a highly unsophisticated client to access the server. Actually playing the game this way will be frustrating – requiring careful URLs to be typed every time and noting the output for further commands. This is quite deliberate. It would be easy to build the web server to allow the whole game to be played seamlessly from a web browser – but this isn’t the point – I want the students to experience building a client themselves and developing that.

For my module last year I gave the students a partially complete client, written in Python, using text only. Their aim for the lab was to complete the client. No client would be exactly the same, but they would all be written to work against the same server specification. In theory a better client will give a player an edge against others – a motivation for improvements. One student built the start of a graphical interface, some needed to be given more working pieces to complete their clients.

I’ve placed a more or less complete client on GitHub as well, but it could certainly do with a number of improvements. I may not make those since the idea of the project is to provide a focus for students to make these improvements.

What Went Well

The server worked reasonably well, and once I got going the code for automatic ship generation and placement worked quite nicely. I built a good number of unit tests for this which flushed out some problems, and which again are intended as a useful teaching resource.

I spent a reasonable amount of time building a server API that couldn’t be too easily exploited. For instance, it’s not possible for any player to be more than one move ahead of another to prevent brute force attacks by a client.

The server makes it relatively easy for as many players as one wants in a given game.

What Went Wrong

I made some missteps the first time around the block.

  • the game grid’s default size is too large – I made this configurable, but too large a grid makes the chances of quick hits much more remote – which the students need for motivation. I only really noticed this when I built my admin view shown above.
  • in the initial game, any hit on a ship destroys it, there is no concept of health, and bigger ships are more vulnerable. This is arguably not a mistake, but is a deviation from expected behaviour.

What’s Next

This year I may add another version of the API that adds health to ships so that multiple hits are needed for large ships. By moving all of this to a new version number in the URL as above, e.g.

I hope to allow for improved versions of the game while still supporting (within reason) older clients accessing the original version.

I might also add admin calls to tweak the number of ships per player and size of the ships. There are defaults in the code, but these can easily be overriden.

Shall We Play A Game?

There’s absolutely no reason the clients need to be written in Python. There’s no reason not to write one in C++ or Java, or to build a client that runs on Android or iOS phones. There’s no reason that first year classes with a text mode Python client can’t play against final year students using an Android client.

If you are teaching in any of these languages and feel like writing new clients against the server please do. If you feel like making your clients open source to help more students (and lecturers) learn more, that would be super too.

If you have some improvements in mind for the server, do feel free to fork it, or put in pull requests.

If you or your class do have a go at this, I’d love to hear from you.


Anatomy of a Puzzle

Recently I was asked to provide a Puzzle For Today for the BBC Radio 4 Today programme which was partially coming as an Outside Broadcast from Ulster University.

I’ve written a post about the puzzle itself, and some of the ramifications of it; this post is really more about the thought process that went into constructing it.

When I was first asked to do this I had a look at the #PuzzleForToday hashtag on Twitter and found that a lot of people found the puzzles pretty hard, so I thought I might try to construct a two part puzzle with one part being relatively easy and the other a bit harder. I also wanted something relatively easy to remember and work out in your head for those people driving, since commuters probably make up a lot of the Today programme audience.

A lot of my students will know I often do puzzles with them in class, but most are quite visual, or are really very classic puzzles, so I needed something else. Trying to find something topical I thought about setting a puzzle around a possible second referendum election, since this was much in the news at the time. My first go at this was coming up with a scenario about an election count, and different ballot counters with different speeds counting a lot of ballots.

I constructed an idea that a Returning Officer had a ballot with a team of people to count votes. One of the team could count all the votes in two hours, the next in four hours, and the next in eight hours. But how long would they take if they worked all together? The second part would be: if there were more counters available but each took twice as long again as the one before, what was the least possible time to complete the task.

I liked this idea because I thought there were a lot of formal and informal ways to get to the answer, and indeed the answers I saw on Twitter and Facebook confirmed this. Perhaps the easiest way to approach the puzzle is this: to consider how much work each can do in an hour. We see the first person gets half of it done, the next one quarter and the next one eighth. All together then:

    \[\frac{1}{2} + \frac{1}{4} + \frac{1}{8} = \frac{7}{8}\]

We need to work out what number we would multiply by this to get one – i.e. the whole job being done. In this case it works out as

    \[1 \div \frac{7}{8} = \frac{8}{7}\]

which works out (a bit imprecisely) with a bit of work, or a decent calculator, as one hour, eight minutes and thirty four seconds and a bit. So, not great, but the second part of the puzzle works out much more smoothly. If we keep on with out pattern, we get

    \[\frac{1}{2} + \frac{1}{4} + \frac{1}{8} + \frac{1}{16} + \frac{1}{32} + \cdots\]

Formally, this is called a Geometric Progression, there is a constant factor between each terms. Sometimes these infinite sums actually have a finite answer, which might be surprising. If you keep adding these fractions you might see that are adding ever closer to 1. Therefore this potentially infinite number of counters gets the work done in one hour, it can’t be less.

So, I was happy the second number worked out nicely, but the first is pretty tricky, and not easily worked out in one’s head. So I wondered what numbers I could use instead that would work out as an exact number of minutes. This really means that my three fractions from the first part of the problem, divide perfectly into 60. I used some Python to help me with this with a list comprehension:

# Establish a top size to work with n = 100
# Find all the triples where
# a,b,c <= n and 1/a + 1/b + 1/c # divides evenly into 60 triples = [(a,b,c) for a in range(1,n+1) for b in range(1,a) for c in range(1,b) if (60/(1/a + 1/b + 1/c)).is_integer()]

It turns out that restricting ourselves to three numbers for the first quiz all of which are under 100 that there are 902 such sets of numbers. The very smallest numbers are 1, 2 and 6. The problem is that most of these triples don’t have the property of my original choices – which that there is a common number multiplied between the first and second, and the second and third etc.. That would make the second part of the puzzle more difficult.

So, I modified my list comprehension a bit to add the condition that there was a common ratio from a to b to c:

# Establish a top size to work with
n = 100

# Find all the triples where a,b,c <= n and 1/a + 1/b + 1/c
# divides evenly into 60 and where the factor between a and b,
# is the same as that between b and c

geometric_triples = [(a,b,c) for a in range(1,n+1)
                     for b in range(1,a)
                     for c in range(1,b)
                     if (60/(1/a + 1/b + 1/c)).is_integer()
                     and a/b == b/c]

This produced just three triples (with all numbers under 100):

>>> geometric_triples
[(28, 14, 7), (56, 28, 14), (84, 42, 21)]

and you can see these are all quite related. So I grabbed the first three numbers to try and keep the puzzle small.

As well as that, the programme team wanted a different focus than an election – they were a bit worried that because it was in the news so much it would be better to have another focus. I considered a computational task divided between processors, but eventually concluded this wouldn’t make a lot of sense to some listeners, so I went with this final configuration of the puzzle.

Part One

A Professor gives her team of three PhD students many calculations to perform. The first student is the most experienced and can complete the job on her own in 7 hours, the next would take 14 hours on his own, and the last would take 28 hours working single handed to complete the task. How long would the task take if they all worked together?

Part Two

If the Professor has more helpers, but which follow the same pattern of numbers to complete the task, what is the absolute minimum time the task can take?

You can probably answer this from the details of the construction above, but if not, you can always cheat here (the BBC programme page) or here.

# Establish a top size to work with n = 100
# Find all the triples where a,b,c <= n and 1/a + 1/b + 1/c
# divides evenly into 60 and where the factor between a and b,
# is the same as that between b and c
geometric_triples = [(a,b,c) for a in range(1,n+1) for b in range(1,a) for c in range(1,b) if (60/(1/a + 1/b + 1/c)).is_integer() and a/b == b/c]


My Puzzle for the Day

In November 2018 the BBC Radio 4 Today Programme was visiting Ulster University for an outside broadcast. I was asked to write the Puzzle for the Day for the broadcast. Here is my puzzle and some discussion about how it can be solved. The puzzle and a very brief solution is on the BBC page, but I restate it below, together with a more full solution.

Part One

A Professor gives her team of three PhD students many calculations to perform. The first student is the most experienced and can complete the job on her own in 7 hours, the next would take 14 hours on his own, and the last would take 28 hours working single handed to complete the task. How long would the task take if they all worked together?

Part Two

If the Professor has more helpers, but which follow the same pattern of numbers to complete the task, what is the absolute minimum time the task can take?

Solving Part One.

Possibly the easiest way to solve the first part is to consider how much work can be done by each team member in a single hour and adding to get the total amount of work done per hour. For instance, the first team member can do the whole job in seven hours, so they can do \frac{1}{7} of the job in one hour etc. So the team together can do the following in one hour:

    \[\frac{1}{7} + \frac{1}{14} + \frac{1}{28} = \frac{4}{28} + \frac{2}{28} + \frac{1}{28} = \frac{7}{28} = \frac{1}{4}.\]

In other words, one quarter of the job can be done in a single hour by the team of three working together. It follows that to do the whole job, the team needs four hours.

Solving Part Two.

So what about part two of the puzzle? Quite reasonably, some people attempting the puzzle assumed that the more and more team members we would add, the time to complete the task would eventually drop to zero. This seems fairly intuitive – if you have potentially infinite team members then the total time must drop to zero.

But imagine we continue our pattern from above, for far more than three team members. This would be the proportion of work done in one hour by even an infinite team.

    \[\frac{1}{7} + \frac{1}{14} + \frac{1}{28} + \frac{1}{56} + \frac{1}{112} + \frac{1}{224} + \cdots \]

If this addition produces an infinite “answer” then an infinite work rate per hour would certainly suggest the task could be done instantly. Surprisingly perhaps this sum of infinitely many numbers does not have an infinite answer. It may be a little easier to see its nature if we take out a factor of \frac{1}{7}.

Then the summation looks like this:

    \[\frac{1}{7} (1 + \frac{1}{2} + \frac{1}{4} + \frac{1}{8} + \frac{1}{16} + \frac{1}{32} + \cdots)\]

It may be intuitive for some people that the numbers inside the bracket will get over closer to 2 – setting aside the initial 1, each subsequent number added goes half way from the previous sum to adding an additional 1 in total. In other words, this whole sum will be \frac{2}{7}.

Some infinite summations do indeed have finite answers, but it can be quite difficult to prove which are which, or to find the summations if they are finite. However, this example, aside from intuitively having a relatively easy answer falls into a special category of such summations called a geometric progression. These are series of the form:

    \[a + ar + ar^2 + ar^3 + ar^4 + \cdots = \]

    \[a (1 + r + r^2 + r^3 + r^4 + \cdots)\]

In other words, each item in the sum is the previous item multiplied by some common ratio r. There is a nice formula for the sum of the first n terms of such progressions which you could use to solve part one – though it would rather be a sledgehammer to crack a nutshell, but there is a formula for the infinite summation too.

    \[S_\infty = \frac{a}{1-r}\]

(provided |r| < 1, or in other words that r is between -1 and 1 not-inclusive, if not then the summation is infinite).

In this case a=\frac{1}{7} and r=\frac{1}{2} and r passes the test above so the infinite sum is indeed verified to be \frac{2}{7}.

Finally therefore, if even infinitely many team members can only do \frac{2}{7} of the job in an hour, we need to work out the number to multiply on this to get to the whole job being done, i.e. not “2/7” of the job but the whole “1” of the job.

That number is \frac{7}{2}, so the whole job can be reduced from the four hours of part one to not less than 3.5 hours.

Of course, there are other ways to solve the puzzle. This is just one example pathway. If you are interested in how I went about constructing the puzzle, I detail that in another post.

Incidentally, the fact that summations of infinitely many objects sometimes has finite answers is of vital importance for many real life applications of mathematics. The whole subject of Integral Calculus relies on this, and for instance, this is closely related to why it is possible, with finite energy for a rocket to escape Earth or an electron to escape an atom.


Review: The Very Short Introduction

Infinity, A Very Short Introduction, by Ian Stewart

This review was originally written for the London Mathematical Society November 2018 Newsletter. The book can be found here.

The “Very Short Introduction” series by Oxford University Press attempt to take a moderately deep dive into various subjects in a slimline volume. Professor Stuart addresses the apparent paradox of tackling the subject of the infinite in such a small volume right at the start, along with the observation that the topic of infinity has long provided such paradoxes. This particular VSI aims to tackle infinity as found in numbers, geometry, art, theology, philosophy and more, and so it is a tightly packed volume indeed.

Infinity is a concept that is, at least now, embraced in Mathematics, but also reaches into Physics, Philosophy, Theology and Language in significant measure. In this book, Ian Stewart sets off almost immediately into the mathematical interpretations and concepts of infinity, starting with examples that are likely to be accessible to a wide range of readers, but also touching on some that will cause more mathematically advanced readers to consider them carefully and which may be challenging to less mathematically literate readers.

These examples are of the paradoxical issues surrounding infinity; all but one of these is explicitly mathematical, some geometrical, others more algebraic; by the end of the first short chapter we have visited David Hilbert’s famous hotel and explored some of the implications for the arithmetic of the infinite.

The second chapter then moves into a more detailed exploration of the consequences of the infinite in numbers, and in particular explores the infinite, non-repeating decimal representations of irrational numbers and the continuity of the real numbers.

Stewart then explores the history of the infinite in the third chapter, and how it weaved through early Greek philosophy and the classic paradoxes of Zeno, and how for the Greeks issues of infinity were closely tied to their thoughts and theories about motion, and indeed whether motion was in fact possible or an illusion. Some time is spent with Aristotle, and how he dismissed the idea of an “actual” infinity in favour of a “potential” infinity. We then move through with both Locke and Kant to the beginning of more modern philosophical analyses of the infinite. Some time is taken to explore the philosophy of the infinite in Christian theology, particularly through Thomas Aquinas, a philosopher heavily influenced by Aristotle and how he used the infinite in his “proof” of God.

Stewart also explores how, in the modern era, mathematicians take the infinite very much as a normal and integral part of mathematics, with little concern about the distinction of actual and potential infinities that were the great concern of the philosophy of the ancient world.

We dive then, from the infinitely large to the infinitely small in the fourth chapter where the seeds of calculus and analysis are to be seen, and the philosophical objections from Bishop Berkeley to the use of infinitesimals. It is interesting to note that these and other concerns about the theoretical underpinnings of calculus were largely ignored in the face of its obvious utility, until others tried to explore these foundations more deeply. Stewart takes us through this work through Cauchy and eventually to the work of Bolzano and Weierstrass who finally introduced the ? and ? notation that has undoubtedly delighted many undergraduates since and ushered in the start of analysis proper. Stewart than dips into an examination of non-standard analysis, a topic that at least I was never knowingly exposed to in my formal studies; it was intriguing to read of these numbers with “standard” and “infinitesimal” parts.

There follows a chapter on the geometrically infinite, which in particular looks at the role of the infinite in art, but which again after an informal discussion dips into the mathematics of what is going on. The chapter after this focuses on infinities that arise in Physics, particularly in optics, Newtonian and Relativistic gravity, moving on then to discuss the size of the known universe and its curvature. These two chapters are both short and may require some unpacking by readers with less background knowledge.

The final chapter is mostly dedicated to work of Cantor and his systemization of modern mathematical thinking around the concept of the infinite. Here we meet the distinctions between the finite, countably infinite and uncountably infinite, transfinite cardinals and transfinite ordinals. But even here we find the objections of some philosophers, in this case Wittgenstein. This is interesting to read in an era where Cantor’s formulations are considered uncontroversial and part and parcel of the “paradise” of Hilbert’s modern mathematics in the same way that the past controversies of complex numbers are of little interest to modern mathematicians.

The approach taken to infinity in the book, is non-apologetically Pure Mathematical in its spirit, and I suppose this may make the work a little less accessible for some readers, particularly those who are not prepared to think through some of the sections, perhaps with a pen and paper. The Very Short Introduction to Mathematics, from the same series, by Timothy Gowers similarly tackles a cross section of challenging examples from the discipline in a relatively small space.

In September 2016, the BBC aired an interesting series on Radio 4: “The History of the Infinite” (this is still happily available online for those interested, at least for those in the UK). In this series, Adrian Moore began discussing the original Greek antipathy to the idea in early philosophy, and then how the idea emerged through Aristotelian Philosophy, Christian theology. It was after this that Moore decided to tackle the more serious implications of the infinitely small and big in mathematics, before emerging back through Physics into more philosophical territory.

I suspect this route, sandwiching the more complicated mathematical treatment between philosophy more related to human experience could be more palatable to a general reader.

The Very Short Introduction to Infinity is nevertheless a fascinating and joyful exploration of the topic, accessible to the committed and careful novice, but with enough detail and asides to delight formally mathematically trained readers.

  • 1

Implementing configurable work-flow patterns in Python Django

In my previous article, I discussed some of changes I’ve made to my WAM software to handle assessment and work-flow. I thought I’d have a look at this from the technical side for those interested in doing something similar, this is obviously extensible to general workflow management, where you might want to tweak the workflow later without diving into code.

My challenge was to consider how not to hard code a work-flow, but to have something that would be configurable, in my case in a SQL layer because I’m using Python and Django.

I had an idea about the work-flow I wanted, and it looked a bit like this (carefully sketched on my tablet). These nodes are particular states, so this isn’t really a flow chart, as decisions aren’t shown. What is shown is what states can progress to the next ones. But I wanted to be able to change the pattern of nodes in the future, or rather, I wanted users to be able to do this without altering the code. I also wanted to work out who could do what, and who should know about what.

Workflow Example
Workflow Example

Understanding States

The first thing I did was to create a State model class, and I guess in my head I was thinking of Markov Models.

class AssessmentState(models.Model):
    """Allows for configurable Assessment Resource Workflow

    name            The name of the state
    description     More details on the state
    actors          The user types who can create this state, CSV field
    notify          The user types to notify that the state has been created
    initial_state   Can this be an initial state?
    next_states     Permissible states to move to from this one
    priority        To allow the states to be sorted for presentation to the user

    actors and notify should be comma separated lists as in USER_TYPES below.

    ANYONE = 'anyone'
    COORDINATOR = 'coordinator'
    MODERATOR = 'moderator'
    EXTERNAL = 'external'
    TEAM_MEMBER = 'team_member'
    ASSESSMENT_STAFF = 'assessment_staff'

    USER_TYPES = (
        (ANYONE, 'Any logged in user'),
        (COORDINATOR, 'Module coordinator'),
        (MODERATOR, 'Module moderator'),
        (TEAM_MEMBER, 'Module teaching team member'),
        (EXTERNAL, 'External Examiner'),
        (ASSESSMENT_STAFF, 'Members of AssessmentStaff'),
        ('exams_office', 'Exams Office')

    name = models.CharField(max_length=200)
    description = models.TextField()
    actors = models.TextField()
    notify = models.TextField()
    initial_state = models.BooleanField(default = False)
    next_states = models.ManyToManyField('self', blank=True, symmetrical=False, related_name='children')
    priority = models.IntegerField()

As you can see, I created variables that told me the name of the state, and an opportunity for a more detailed description. I then wanted to be able to specify who could do certain things, and be notified. So, rather than a long series of Booleans, I want for a text field – the work-flow won’t be edited very often, and when it is, it should be by someone who knows what they are doing. So it’s just a Comma Separated text field. For instance.


will indicate that the Module Coordinator and Moderator should be involved (this is an HE example, but the principle is quite extensible).

So the actors field will specify which kinds of people can invoke this state, and the notify field those who should get to hear about it.

I want to draw your attention to this bit:

next_states = models.ManyToManyField('self', blank=True, symmetrical=False, related_name='children')

What on earth does this do? It allows a Django model to have a Many to Many relationship with itself. In other words, for me to associate a number of states with this one. Please also note that presence of


This is most easily explained by comparison to the Facebook and Twitter friendship model. Both of these essentially link a User model in a many to many relationship with itself.

Facebook friends are symmetrical, once the link is established, it is two way. Twitter followers are not symmetrical.

I wanted to establish which successor states could be invoked from any given one. And this should not be symmetrical by default. You can see in my example graph above, I want it to be possible to move from state A to either B or C, but this is not entirely symmetric, it is possible to move from B to A, but it should not be possible to go from C to A. Without symmetric=False, each link will create an implied link back (all arrows in my state diagram would be bi-directional) which would be problematic. By establishing the relationship as asymmetric we can allow a reciprocal link (as is possible in Twitter, and our A and B example), but we don’t enforce it, so that we prevent back tracking in work-flow where it should not be allowed (as in our A and C example).

Invoking States

I then created another model to keep track of which states were invoked.

class AssessmentStateSignOff(models.Model):
    """Marks a particular sign off of an AssessmentState for a module

    module              The module being signed off
    assessment_state    The AssessmentState being used
    signed_by           The user signing off (Could be Staff or Examiner)
    created             The datestamp for signoff
    notified            The datestamp for notificiations sent
    notes               Any comments

    module = models.ForeignKey(Module, on_delete=models.CASCADE)
    assessment_state = models.ForeignKey(AssessmentState, on_delete=models.CASCADE)
    signed_by = models.ForeignKey(User, on_delete=models.CASCADE)
    created = models.DateTimeField(auto_now_add=True)
    notified = models.DateTimeField(null=True, blank=True)
    notes = models.TextField()

    def __str__(self):
        return str (self.assessment_state)

This model allows me to work out who (the signed_by field) invoked a particular AssessmentState, when, and with any particular notes.

I also added a field to record when a notification (notified) had been sent. On creation, I leave that field as null. One of the many glorious things about Django is that it’s infrastructure for custom management commands allows you to easily build command line tools for doing cron tasks while your web front end runs without interruption. I found this rather awkward, but not impossible, in PHP, but in Django the whole thing is very organic, and you get access to all your models. If you have pushed plenty of your logic into the Model layer and not the View layer, this can really help.

In my new custom commend I can easily work out which signoffs have not been notified yet:

# Get all sign offs with no notification time
signoffs = AssessmentStateSignOff.objects.all().filter(notified=None).order_by("created")

I can then act upon those, send notifications, and if that’s successful, set the notified field to the time at which I sent them.

Further Reading

In this article I have concentrated on the Model layer, with a few other observations, and in particular the relationship from a State model to itself.

All of the Forms and Views are available within my GitHub repository for the project. They aren’t a work of art, but if you have any questions feel free to look there, or get in touch.

I hope that might be helpful to someone facing the same challenge, and do feel free to suggest how I could have solved the problem more elegantly.

  • 2

Assessment handling and Assessment Workflow in WAM

Sometime ago I began writing a Workload Allocation Modeller aimed at Higher Education, and I’ve written some previous blog articles about this.

As is often the way, the scope of the project broadened and I found myself writing in support for handling assessments and the QA processes around them. At some point this necessitates a new name for WAM to something more general (answers on a post card please) but for now, development continues.

Last year I added features to allow Exams, Coursework, and their Moderation and QA documents to be uploaded to WAM. This was generally reasonably successful, but a bit clunky. We gave several External Examiners access to the system and they were able to look in at the modules for which they were an examiner and the feedback was pretty good.

What Worked

One of the things that worked best about last year’s experiment was that we put in information about the Programmes (Courses) each Module was on. It’s not at all unusual for many Programmes to have the same Module within them.

This can cause a headache for External Examination since an External Examiner is normally assigned to a Programme. In short, the same Module can end up being looked at by several Examiners. While this is OK, it can be wasteful of work, and creates potential problems when two Examiners have a different perspective on the Module.

So within WAM, I put in code an assumption of what we should be doing in paper based systems – that every Module should have a “Lead Programme”. The examiner for that Programme should be the one that has primacy, and furthermore, where they are presented other Modules on the Programme for which they aren’t the “lead” Examiner, they should know that this is for information, and they may not be required to delve into it in so much detail – unless they choose to.

This aspect worked well, and the External Examiners have a landing screen that shows which Modules they are examining, and which they are the lead Examiner.

What Didn’t Work

I had written code that was intended to look at what assessment artefacts had been uploaded since a last user’s login, and email them the relevant stuff.

This turned out to be problematic, partly because one had to unpick who should get what, but mostly because I’m using remote authentication with Django (the Python framework in which WAM is written), and it seems that the last login time isn’t always updated properly when you aren’t using Django’s built in authentication.

But the biggest problem was a lack of any workflow. This was a bit deliberate since I didn’t want to hardcode my School or Faculty’s workflow.

You should never design your software product for HE around your own University too tightly. Because your own University will be a different University in two years’ time.

So, I wanted to ponder this a bit. It made visibility of what was going on a little difficult. It looked a bit like this (not exactly, as this is a screenshot from a newer version of an older module):

Old view of Assessment Items
Old view of Assessment Items

with items shown from oldest at the bottom to newest at the top. You can kind of infer the workflow state by the top item, and indeed, I used that in the module list.

But staff uploaded files they wanted to delete (and that was previously disallowed for audit reasons) and the workflow wasn’t too clear and that made notifications more difficult.

What’s New

So, in a beta version of 2.0 of the software I have implemented a workflow model. I did this by:

  • defining a model that represented the potential states a Module could be in, each state defines who can trigger it, and what can happen next, and who should be notified;
  • defining a model that shows a “sign off” event.

Once it became possible to issue a “sign off” of where we were in the workflow, a lot of things became easier. This screenshot shows how it looks now.

Example of new assessment workflow
Example of new assessment workflow

Ok, it’s a bit of a dumb example, since I’m the only user triggering states here (and I can only do that in some cases since I’m a Superuser, otherwise some states can only be triggered by the correct stakeholder – the moderator of examiner).

However, you can see that now we can still have all the assessment resources, but with sign offs at various stages. The sign off could (and likely would) have much more detailed notes in a real implementation.

This in turn has made notification emails much easier to create. Here is the email triggered by the final sign off above.

The detailed notes aren’t shown in the email, in case other eyes are on it and there are sensitive comments.

All of this code is available at GitHub. It’s working now, but I’m probably do a few more bits before an official 2.0 release.

I will be demoing the system at the Royal Academy of Engineering in London next Monday, although that will focus entirely on WAM’s workload features.

  • 8

Migrating Django Migrations to Django 2.x

Django is a Python framework for making web applications, and its impressive in its completeness, flexibility and power for speedy prototyping.

It’s also an impressive project for forward planning, it has a kind of built in “lint” functionality that warns about deprecated code that will be disallowed in future versions.

As a result when Django 2.0 was released I didn’t have to make many changes to my app code base to get it to work successfully. However, today when I tried to update my oldest Django App (started in Django 1.8x) I hit an unexpected snag. The old migrations were sometimes invalid. Curiously I don’t think this problem emerged the last time I tried.

Django uses migrations to move the database schema from one version to the next. Most of the time it’s a wonderful system. In the rare case it goes wrong it can be … tricky. Today’s problem is quite specific, and easier to fix.

Django 2.0 enforces that ForeignKey fields explicitly specify a behaviour to follow on deletion of the object pointed to by the key. In general whether we Cascade the deletion, or set the field to Null, getting the behaviour write can be important, particular on fields where a Null value has a legitimate meaning.

But a bit of a sting in the tail is that an older Django project may have migrations created automatically by Django which don’t obey this. I discovered this today and found I couldn’t proceed with my project unless I went back and modified the old migrations to be 2.0 compliant.

So if this happens to you, here are some suggestions on fixing the problem.

You will know if you have a problem if when you try to run your test server, or indeed replace runserver by check

python3 runserver

you get an error and output like this

  File "/Users/colin/Development/WAM/WAM/loads/migrations/", line 7, in <module>
    class Migration(migrations.Migration):
  File "/Users/colin/Development/WAM/WAM/loads/migrations/", line 100, in Migration
    field=models.ForeignKey(null=True, to='loads.ActivitySet', blank=True),
TypeError: __init__() missing 1 required positional argument: 'on_delete'

I would suggest you try runserver whatever you did before as it will continue to try each time you save a file.

Open your code with your favourite editor, and open your file (you may have several depending on your project), and the migration file that’s broken as above.

Looking in your migration file you’ll find the offending line. In this case it’s the last (non trivial) line below.

            field=models.ForeignKey(null=True, to='loads.ActivitySet', blank=True),

To ensure that your migrations will be applied consistently with your final model (well, as long as nobody tries to migrate to an intermediate state) look carefully in the correct model (Activity) in this case, and see what decision you make for deletion there. In my case I want deletion of the ActivitySet to kill all linked Activitiy(s). So replicate the “on_delete” choice from there.

            field=models.ForeignKey(null=True, to='loads.ActivitySet', on_delete=models.CASCADE, blank=True),

Each time you save your new migration file the runserver terminal window will re-run the check, hopefully moving on to the next migration that needs to be fixed. Work your way through methodically until your code checks clean. Check into source control, and you’re done.



Semi Open Book Exams

A few years ago, I switched one of my first year courses to use what I call a semi-open-book approach.

Open-book exams of course allow students to bring whatever materials they wish into them, but they have the disadvantage that students will often bring in materials that they have not studied in detail, or even at all. In such cases, sifting through materials to help them answer a question could be counter productive.

On the other hand, the real world is now an increasingly “open-book” environment, which huge amounts of information available to those in the workplace which is now almost always Internet connected.

So I decided to look at another approach. Students are allowed to bring in a single, personalised, A4 sheet, on which they can write whatever they wish on both sides. There are a few rules:

  • the sheet must be written on “by hand”, that is to say, it cannot be printed to from a computer, or typed;
  • the sheet must be “original”, that is to say, it cannot be a photocopy of another sheet (though students may of course copy their original for reference);
  • the sheet must be the student’s own work, and they must formally declare as much (with a tick box);
  • the sheet must be handed in with the exam paper, although it is not marked.

The purpose of these restrictions are to ensure that each student takes a lead in producing an individual sheet, and to inhibit cottage industries of copied sheets.

In terms of what can go on the sheet? Well anything really. It can be sections from notes, important formulae, sample questions or solutions. The main purpose here is to prompt students to work out what they would individually distill down to an A4 page. So they go through all the module notes, tutorial problems and more, and work out the most valuable material that deserves to go on one A4 page. I believe that this process itself is the greatest value of the sheet, its production rather than its existence in the exam. I’m working on some research to test this.

So I email them each an A4 PDF, which they can print out at home, and on whatever colour paper they may desire. The sheet is individual and has their student number on it with a barcode, for automated processing and analysis afterwards for a project I’m working on, but this is anonymised. The student’s name in particular does not appear, since in Ulster University, it does not appear on the exam booklet.

The top of my sheet looks like this:

The top of a sample guide sheet.

So, if you would like to do the same, I am enclosing the Python script, and LaTeX that I use to achieve this. You could of course use any other technology, or not individualise the sheet at all.

For convenience the most recent code will also be placed on a GitHub repository here, feel free to clone away.

My script has just been rewritten for Python 3.x, and I’ve added a lot of command line parameters to decouple it from me and Ulster University only use. It opens a CSV file from my University which contains student id numbers, student names, and emails in specific columns. These are the default for the script but can be changed. For each student it uses LaTeX to generate the page. It actually creates inserts for each student of the name and student number, you can then edit open-book.tex to allow the page to be as you wish it. You don’t need to know much LaTeX to achieve this, but ping me if you need help. I am also using a LaTeX package to create the barcodes automatically.

I’ve spent a bit of time adding command line parameters to this script, but you can try using

python3 --help

for information. The script has been rewritten for Python 3. If you run it without parameters it will enter interactive mode and prompt you.

I’d strongly recommend running with the –test-only option at first to make sure all looks good, and opening open-book.pdf will show you the last generated page so you can see it’s what you want.

Anyway, feel free to do your own thing, or mutilate the code. Enjoy!

#!/usr/bin/env python

# Copyright Colin Turner 2014-2017
# Free and Open Source Software under GPL v3
import argparse

import csv
import re
import subprocess
import smtplib
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def process_student(args, row):
    """Takes a line from the CSV file, you will likely need to edit aspects of this."""
    'kicks of the processing of a single student'
    student_number = row[args.student_id_column]
    student_name = row[args.student_name_column]
    student_email = row[args.student_email_column]
    print('  Processing:', student_name , ':', student_email)
    create_latex_inserts(student_number, student_name)
    send_email(args, student_name, student_email)

def create_latex_inserts(student_number, student_name):
    """Write LaTeX inserts for the barcode and student name

    For each student this will create two tiny LaTeX files:

     * open-book-insert-barcode.tex which contains the LaTeX code for a barcode representing the student number
     * open-book-insert-name.tex which will contain simply the student's name

    These files can be included/inputted from open-book.tex as desired to personalise that document

    student_number is the ID in the students record system for the student
    student_name is the name of the student"""

    # Open a tiny LaTeX file to put this in
    file = open('open-book-insert-barcode.tex', 'w')

    # All the file contains is LaTeX to code to create the bar code
    string = '\psbarcode{' + student_number + '}{includetext height=0.25}{code39}'

    # The same exercise for the second file to contain the student name
    file = open('open-book-insert-name.tex', 'w')
    string = student_name

def create_pdf():
    """Calls LaTeX and dvipdf to create the personalised PDF with inserts from create_latex_inserts()"""

    # Suppress stdout, but we leave stderr enabled."latex open-book", stdout=subprocess.DEVNULL, shell=True)"dvipdf open-book", stdout=subprocess.DEVNULL, shell=True)

def send_email(args, student_name, student_email):
    """Emails a single student with the generated PDF."""
    #TODO: Might be useful to improve the to address
    #TODO: Allow subject to be tailored.

    subject = args.email_subject
    from_address = args.email_sender
    # to_address = student_name + ' <' + student_email + '>'
    to_address = student_email

    msg = MIMEMultipart()
    msg['Subject'] = subject
    msg['From'] = from_address
    msg['To'] = to_address

    text = 'Dear Student\nPlease find enclosed your guide sheet template for the exam. Read the following email carefully.\n'
    part1 = MIMEText(text, 'plain')

    # Open the files in binary mode.  Let the MIMEImage class automatically
    # guess the specific image type.
    fp = open('open-book.pdf', 'rb')
    img = MIMEApplication(, 'pdf')


    # Send the email via our own SMTP server, if we are not testing.
    if not args.test_only:
        s = smtplib.SMTP(args.smtp_server)
        s.sendmail(from_address, to_address, msg.as_string())

def override_arguments(args):
    """If necessary, prompt for arguments and override them

    Takes, as input, args from an ArgumentParser and returns the same after processing or overrides.

    # If the user enabled batch mode, we disable interactive mode
    if args.batch_mode:
        args.interactive_mode = False

    if args.interactive_mode:
        override = input("CSV filename? default=[{}] :".format(args.input_file))
        if len(override):
            args.input_file = override

        override = input("Student ID Column? default=[{}] :".format(args.student_id_column))
        if len(override):
            args.student_id_column = int(override)

        override = input("Student Name Column? default=[{}] :".format(args.student_name_column))
        if len(override):
            args.student_name_column = int(override)

        override = input("Student Email Column? default=[{}] :".format(args.student_email_column))
        if len(override):
            args.student_email_column = int(override)

        override = input("Student ID Regular Expression? default=[{}] :".format(args.student_id_regexp))
        if len(override):
            args.student_id_regexp = override

        override = input("SMTP Server? default=[{}] :".format(args.smtp_server))
        if len(override):
            args.smtp_server = override

        override = input("Email subject? default=[{}] :".format(args.email_subject))
        if len(override):
            args.email_subject = override

        override = input("Email sender address? default=[{}] :".format(args.email_sender))
        if len(override):
            args.email_sender = override


def parse_arguments():
    """Get all the command line arguments for the file and return the args from an ArgumentParser"""

    parser = argparse.ArgumentParser(
        description="A script to email students study pages for a semi-open book exam",
        epilog="Note that column count arguments start from zero."


    parser.add_argument('-b', '--batch-mode',
                        help='run automatically with values given')

                        help='prompt the user for details (default)')

    parser.add_argument('-i', '--input-file',
                        help='the name of the input CSV file with one row per student')

    parser.add_argument('-sidc', '--student-id-column',
                        help='the column containing the student id (default 1)')

    parser.add_argument('-snc', '--student-name-column',
                        help='the column containing the student name (default 2)')

    parser.add_argument('-sec', '--student-email-column',
                        help='the column containing the student email (default 9)')

    parser.add_argument('-sidregexp', '--student-id-regexp',
                        help='a regular expression for valid student IDs (default B[0-9]+)')

                        help='the address of an smtp server')

                        default='IMPORTANT: Your semi-open-book Guide Sheet',
                        help='the subject of emails that are sent')

                        help='the sender address from which to send emails')

    parser.add_argument('-t', '--test-only',
                        help='do not send any emails')

    args = parser.parse_args()

    # Allow for any overrides from program logic or interaction with the user
    args = override_arguments(args)

def main():
    """the main function that kicks everything else off"""

    args = parse_arguments()

    print("Starting open-book...")
    csvReader = csv.reader(open(args.input_file, 'r'), dialect='excel')

    student_count = 0
    # Go through each row
    for row in csvReader:
        student_number = row[args.student_id_column]
        # Check if the second cell looks like a student number
        if re.match(args.student_id_regexp, row[args.student_id_column]):
            student_count = student_count + 1
            process_student(args, row)
            print('  Skipping: non matching row')

    print('Stopping open-book...')

if __name__ == '__main__':

I use a LaTeX template for the base information, this can be easily edited for taste.


% Does it all have to be Arial now? <sigh>

\author{Professor Colin Turner}
\textbf{EEE122 Examination Guide Sheet}

This sheet, and its contents that you have added, can be brought into
the examination for EEE122. The contents \textbf{must} be compiled
by yourself, be handwritten, and be original (i.e. \textbf{NOT} 
photocopied or similar). You may use the
reverse side. You may retain a copy you have made before the examination
but the original must be handed in with your examination scripts at the
end of your examination.

% D'Oh! Not supposed to put a name on anything going in the exam.
%}{includetext height=0.25}{code39}

Please read the following declaration and tick the box to indicate you agree:

I declare this sheet to have been compiled by myself and not by another, and that the student number above is mine.

%\rule[-1 cm]{10 cm}{1 pt}
\framebox[0.3 cm]{ }



The Most Dangerous Idea in History

In the modern world we often throw around the word meme to mean some comic image, video or idea that has become associated with a concept, but the word has a different origin.

“an element of a culture or system of behaviour passed from one individual to another by imitation or other non-genetic means.”

This usage was coined by Richard Dawkins in his 1976 book “The selfish gene“. Like genes, memes are replicated by one process or another, sometimes with mutations. Like genes, memes are subject to a form of “evolutionary pressure”, a survival of the fittest.

So memes are not just ideas, but ideas can be seen as memes. I’ll likely use the words a bit interchangeably for convenience, however, in this article. The best ideas or memes, can survive for centuries or millennia, as Dawkins himself noted:

“But if you contribute to the world’s culture, if you have a good idea…it may live on, intact, long after your genes have dissolved in the common pool. Socrates may or may not have a gene or two alive in the world today, as G.C. Williams has remarked, but who cares? The meme-complexes of Socrates, Leonardo, Copernicus and Marconi are still going strong.”

In effect, memes can be more immortal and long-lasting then genes. And the transmission can be more direct as well. You may not descend from Socrates, Newton or Curie, whatever benefit that may or may not give you, but you can easily open a book and have those memes transmitted to you directly (or more likely through one or two intermediaries) very efficiently.

This is a very important feature of humanity, perhaps its most important: the ability of a human to learn from more than just its immediate family or peer group, however valuable that interaction is.

Some people have explored the viral nature of memes, and in this sense, we can easily understand that in terms of the common usage of a word in social media.

Of course, some memes are millennia old, have virally spread and are just plain wrong. The popular meme that humans have five senses is wrong. So memes can survive selection pressure despite error. Of course, the pressure may be more intense and effective if the consequences are more significant.

I contend that the most dangerous idea in history is one of a family of related ideas on this theme:

It is bad / wrong / sinful / wicked to question / doubt.

This is a very widespread meme indeed, and a successful one therefore in terms of its own survival. It exists in various strengths and in various contexts. And it doesn’t seem especially dangerous; it’s an innocuous statement.

So what’s the problem? Well, there are two aspects to this.

Firstly, it knocks out your mental immune system. This idea is almost parasitic because it reinforces itself with circular logic. Once it is in place, it prevents or inhibits its own eviction. After all, one has to challenge the idea to reject it, and the mind the idea resides in has already accepted that this is unacceptable.

People that have been taught this as part of their philosophy, ethics or morality, will of course tend to pass it on as a necessary element in those systems, and one can see why.

Because the second aspect is that, this idea rarely comes on its own. The really big problem with this idea is that explicitly or implicitly it tends to actually be found in this form:

It is bad / wrong / sinful / wicked to question / doubt [X].

And then X is or can be the problem. In other words, this is a mental virus that often comes with an associated payload. Like a two-part drug.

For convenience, let’s call X the “payload“, the idea or collection of ideas that hitches a ride with the “immunosuppressant“, the idea that one must not question the payload.

It’s the other part of the coupling that makes the first part dangerous.

Maybe the payload is trivial, like somebody learning a martial art who has essentially been told not to question anything in what they are being taught. In such cases the immunosuppressant challenge could cause poor form or technique never to really be corrected, or not to be open to improvement from ideas from others.

Surprisingly one can find the immunosuppressant quite easily in class rooms, where groups of students have been told that they have to accomplish a task by certain means are exhorted not to think about why. Or sometimes they are so frightened out of asking questions that they pick up the immunosuppressant meme all by themselves. This can damage their ability to discern good ideas from bad.

If the payload is something more serious, such as having significant ethical or moral content then it might still be a relatively minor problem. For example if the payload is ethically benign such as some variation on the Golden Rule, then few issues arise, since the immunosuppressant defeating aspect is reinforcing a behaviour (payload) that is ethically non damaging or even perhaps, life enhancing.

But, if the payload contains many ethically or morally dubious aspects, then you have real problems, because these ideas and behaviours simply cannot be challenged from outside that mind. If the person swallowing the two part pill has accepted the immunosuppressant wholeheartedly then almost nothing can be done to recover that mind’s proper function. It’s trivially easy to see this at work in the world, where people of a given faith can’t even accept that adherents of different strands of that faith are worthy of respect, or in extreme cases, life itself.

In most cases the payload is complex, comprising both good and bad ideas; in these cases the immunosuppressant is the main reason preventing people from discerning which bits to hang on to and which bits to discard. Fortunately, for many the immunosuppressant isn’t full strength, and they quietly, and quite sensibly work out which parts of the payload to discard, but often with no fanfare. They are sometimes still ashamed to state that they do this or don’t even admit it to themselves.

But we shouldn’t be embarrassed to say that parts of a payload are good and parts should be rejected. For instance, most people of faith, from the Abrahamic tradition, quietly reject parts of the payload, let’s take this one:

“If a man has sexual relations with a man as one does with a woman, both of them have done what is detestable. They are to be put to death; their blood will be on their own heads.”

I mean, there’s no getting around it. It’s perfectly clear, in the payload, and it’s equally clear to most 21st century people that this is wrong. Wrong. Illegal. Murder. Ludicrous even. But still many lovely and kind people will try and apologise for this, quoting nicer parts of the payload, rather than just admitting that this is wrong, often because the immunosuppressant part of the pill says we have to not question any aspects of the payload.

And then we are surprised when people kill each other around the world based on the differences in their ideas, even if those differences are trivial, and pose absolutely no threat whatsoever.

But why should we be surprised? The answer is all too obvious.

Horrifically, in many cases, they have been explicitly told to do these things. It’s there in writing. And the immunosuppressant is strongly in place. It’s no good saying that the payload has lots of nice bits in it too. That’s great. That’s wonderful, but the payload will only become better when people are able to admit that parts of it are just plain wrong, and need to be rejected. For this to happen, the immunosuppressant has to be removed. At this point their natural mental immune response comes back to life. It is then possible for peers to influence people for the best. It is easier and possible to learn from the positive examples of others.

If we want to rid ourselves of some of the worst most horrific memes of our past, we need to admit that this is a possibility, and this is why doubt and questioning isn’t a sin, or an error, but the most basic principle of mental hygiene.

“The unexamined life is not worth living.” Socrates


Maths, Software, Hardware, Martial Arts and more