Knitting is an acceptable Lisp

So, I'm a knitter. And I pretend to be a designer some days, when I'm not knitting endless swathes of k2, p2 rib. But first and foremost, I'm an unabashed geek.

As such, I took a look at KnitML. And it's neat. Well, in theory, it's neat. In practice, I haven't got the java dependencies set up on my computer yet to play with it. I do intend to do that, and am downloading them, but while I wait for them, I want to ponder through an idea that's been percolating in my brain.

Namely, I think of knitting patterns in two ways. One is the chart. This is how I write patterns, really. And given the choice between a chart and a list of stitches, I'll pick the chart every time.

It's a question of information density. The chart presents all the information that I need, in the least amount of space while still being human-readable.

So, let's look at a chart.

It's a fairly simple pattern, embedded moss stitch rib. And can be easily transformed into a written pattern:

  1. p2, k2, p1, k1, repeat to end of round
  2. p2, k2, p1, k1, repeat to end of round
  3. p2, k1, p1, k2, repeat to end of round
  4. p2, k1, p1, k2, repeat to end of round

This is where my brain starts making clicky noises. See, my immediate reaction to that is that it's a simple program. One that generates a handsome little ribbing.

However, my second line of thought runs straight for a programming principle: Don't Repeat Yourself. My process for generating a pattern is to draw charts in Inkscape, test those charts by knitting them, then translate the chart into written instructions. In future, I may also want to make my charts available in KnitML.

What this immediately says to me as a programmer is that I should have a single piece of data from which I can generate:

  • PNG graphics
    This is the format I use to show charts. It's a useful format in that it does a good job of reducing low-colour line drawings (like knitting charts) to a fairly small file size with no loss of detail or crispness.
    This is an even worse format to use as a data source, though. Rather than just interpreting the XML that SVG gives, I'd have to start into OCR territory.
  • SVG graphics
    This is the format that I actually use to make charts. Unfortunately, it would be a considerable effort to use it as a starting point: Anything more complicated than a knit or purl stitch are relatively complex "groups", rather than simple objects.
    However, they work admirably as a way to draw charts, and from my perusal of the spec, should be able to be drawn programmatically fairly easily.
  • HTML lists
    This actually isn't a bad starting point. Not good enough that I'd want to use it as a data source, but not bad enough, either, to discard it out of hand.
    Specifically, if I look at the HTML describing the pattern:
    <ol>
      <li> p2, k2, p1, k1, repeat to end of round
      <li> p2, k2, p1, k1, repeat to end of round
      <li> p2, k1, p1, k2, repeat to end of round
      <li> p2, k1, p1, k2, repeat to end of round
    </ol>
    Other than the <ol> and <li> tags, this actually isn't a bad representation of the data at all.
  • Plaintext
    What if I were to use the plaintext representation of the aforementioned list:
    p2, k2, p1, k1, repeat to end of round
    p2, k2, p1, k1, repeat to end of round
    p2, k1, p1, k2, repeat to end of round
    p2, k1, p1, k2, repeat to end of round
    That's actually, as I alluded before, a programmatic representation of knitting. And, were it not for that pesky "repeat to end of round", would be nearly perfect.
    In fact, this is relatively close to what KnitML uses as a human-readable format, according to my reading of the spec.
  • S-Expressions
    These little gems are the core of Lisp, and are easily transformable to and from XML if the need arises.
    The core elements to them are lists and atoms. A list is multiple atoms surrounded by parentheses and separated by spaces. An atom is some text representing something.
    Thus, the pattern described above becomes:
    (repeat 2
      (repeat-to-end (p2 k2 p1 k1)))
    (repeat 2
      (repeat-to-end (p2 k1 p1 k2)))
    And any Lisp programmer reading this just started twitching. As that's not properly-formed Lisp. On the other hand, it does represent a very interesting way to mix data and instructions.
    Patterns become lists of stitches and transformative operations performed on those stitches. It's knitting as a pseudo-mathematical notation.

So, taking s-expressions as a base, what does it all mean?

Well, in that list of representations, the further along I went, the more the representation changed from being a depiction of the data to a representation. By the time we reach the plaintext version of the pattern, it is recognisable as a rudimentary program.

The s-expression version isn't even a rudimentary one; if it were fed into an interpreter that recognised the words and symbols used, it is a program to generate a knitting pattern.

Now that offers some interesting prospects, because the output of the interpreter by no means has to be fixed. For instance, I could have one interpreter that reads the pattern as a way to generate a plain text file, another interpreter that generates an SVG graphic and a third that reads the pattern as a way to generate the equivalent KnitML file.

Why does all this matter? Well, I've become interested, in the past weeks, in the idea of an editor where I type in a row of stitches and it appears on the screen as a graphical pattern. Thinking about how to represent stitches is a first step on the road to knitting pattern zen.

Picking up a knit selvedge

This is a step that I first tried two months ago. I was pleasantly surprised that it was far less difficult than I was anticipating.

Preparation

You'll need the following:
  • Needles smaller than the ones you intend to use, of the same type (straight, circular or double-pointed.)
  • Knowledge of your vertical gauge (rows/cm) in the material from which you're picking up and your horizontal gauge (stitches/cm) with the yarn and needles that you'll be using to knit new material.
    It is utterly vital that your gauge be over the same measurement. Further, this will be a lot easier if the number of stitches is a whole number. 3.5 rows / cm is much harder to work with than 7 rows / 2 cm.

Picking Up

For the sake of discussion, I'll presume that your stitches on the fabric from which you're picking up run left to right. That is to say that the material has been rotated ninety degrees clockwise. If it's turned the other way, reverse all lefts and right in the directions.

Looking at the fabric, the knit stitches should make "V"s, all pointing left. Directly above those should be your selvedge stitches. Between those, above the point of the "V", there is a hole in the fabric.

Insert one needle into this hole, then the other one beneath it. There should be two strands of yarn above the needles, as you're inserting the needles between two knit stitches. Knit one stitch.

Your new stitches should pull slightly to the right, making the hole through which you knit larger and more visible. Fortunately, as you pick up the other selvedge stitches, this becomes less visible.

Repeat this process with the next stitch, inserting the needle at the hole left of the knit "V" and knitting another stitch. This will give you one picked-up stitch per row.

Equalizing Sizes

There's a problem here, as you might guess from the gauge measurements. Namely, vertical gauge is different from horizontal gauge. There's two ways to solve that problem, one easy, the other giving more control over the fabric.

To cover the easy way first, simply knit the new material on needles where your horizontal gauge is the same as the vertical gauge on the fabric from which you're picking up. That is to say, if your vertical gauge is 5 rows / 2 cm, increase the needle size to ones where you have a horizontal gauge of 5 stitches / 2 cm.

Simple enough. The drawback is that, by necessity, the new fabric will be looser than the fabric from which you were picking up. If that doesn't bother you, carry on. If it does, read on.

To figure out how the fabric has to change requires a little math.

( stitches / cm ) / ( rows / cm ) == stitches / row

So, for example, if I have a horizontal gauge of 9 stitches / 2 cm and a vertical gauge of 7 rows / 2 cm, it works out as:

( 9 stitches / 2 cm ) / ( 7 rows / 2 cm ) == 9 / 7 stitches / row

The above is why I emphasized that the measurements need to be over the same distance. So long as your measurements are the same distance, the distances cancel out. It's also why the number of stitches need to be whole numbers: to avoid having fractions like 4.5 / 3.5. This is important because later on, these numbers are used for counting stitches, and 4.5 stitches is not a valid count.

If, on the other hand, I'm going from 24 rows / 5 cm to 8 stitches / 5 cm (small needles to larger ones), I get the calculation:

( 8 stitches / 5 cm ) / ( 24 rows / 5 cm ) == 8 / 24 stitches / row

Hopefully, it's evident that this part is simply creating a fraction with horizontal gauge over vertical gauge. There's nothing more to it if your measurement is the same.

If you want, reduce the fraction, so that both halves are as small as possible. This is only necessary for mathematical correctness (or ease of counting) though, so if you don't remember how or don't want to bother, leave it as it is.

Increasing

If the numerator (top part of the fraction) is greater than the denominator (bottom part of the fraction), that indicates that you need to increase the number of stitches to meet gauge.

Subtract the denominator from the numerator. The result is how many stitches you have to gain.

If I start with 9 / 7 stitches / row:

numerator - denominator == 9 - 7 == 2

So in that example, I need to gain 2 stitches over every 7. In other words, in every 7 stitches knit, I need two increases.

Decreasing

If the numerator (top part of the fraction) is less than the denominator (bottom part of the fraction), you'll need to decrease the number of stitches to meet gauge.

Subtract the numerator from the denominator. The result is how many stitches you need to lose. The result times 2 (because a decrease takes 2 stitches and makes them into 1 is how many stitches are required to make that many simple decreases.

If I start with 8 / 24 stitches / row:

denominator - numerator == 24 - 8 == 16

16 * 2 == 32

The result tells me that I need to lose 16 stitches, meaning that I need 32 stitches to decrease, or that I need to start rethinking needle sizes.

If I start with the more reasonable 7 / 9 stitches / row:

denominator - numerator == 9 - 7 == 2

2 * 2 == 4

This time, I need to lose two stitches every 9. That is to say, I need to decrease over 4 stitches, making them into 2. Because 4 is less than 9, this change in gauge is plausible.

Actually changing the number of stitches

The easiest way is to either knit or purl a row (depending on the rest of the pattern). Work all the increases or decreases on this row, which should sit close to the original fabric.

If you're working increases, do so with an eye to the bottom of the pattern, that way a stitch knit front and back can become two knit stitches on the next row.

In either case, it looks neater if the increases and decreases are spread out across the pattern, rather than being clustered together.