Saturday 25 October 2014

Cut and Paste

I replied on Twitter to Grady Booch and Valentin T Mocanu earlier (Booch in turn was commenting on a Quora answer). Being Twitter it was a bit brief and slightly flippant. The thrust was "cut and past programming is fine" ... "fine for the first 2-3 years".
My contribution: using example code teaches what you ask of it.

I've learnt a lot with examples; I've taught a lot with examples. But, what I learnt by using examples was not how to touch type from a magazine, nor how to copy and paste from examples. Like most programmers that work with others, I look at what others write. Sometimes it gets me through a block. Sometimes I see something new. As I use it I mentally fit it to my existing models. Then I prod what I've got, see what breaks it, how it changes, what else the documentation says about this new thing.

When I use a new library or service I often reach for example code. OOP is very good at giving us generality: lots of classes, lots of methods. But the first use of something is usually pretty close to the common case. API documentation rarely describes that (many thanks to those that do!) and simplifying the common case sometimes gets lost in the quest for elegance. Examples show that path. Then, as before, documentation can be explored to vary from this. Exploration gives a structure to build a model from.

Testing, of course, is a great tool for doing this exploration, prodding an idea and seeing what happens. Asking "my code does X, but what I really want is Y, how do I get there?".

Blind cut and paste, of course, teaches next to nothing and doesn't solve all problems. You can get by on it, but it would be hard to tread a new path that way. I see this frequently, and in many ways it puzzles me. One of the great attractions of programming, to me as a kid, was that I had an infinite set of problems to solve to make the computer do something fun. And, unlike electronics which I also enjoyed, the chances of my programming breaking the computer in a way I couldn't fix were small. (Of course if you ask the 13 year old me you wouldn't get that as an answer!) But, some people get the fear when you suggest that they try something new with some example code. I don't think it is fear of breaking it that is the barrier really, but a self-perception of incompetence - much as is involved in shyness. A lack of belief that they have the mental tools to make that exploration, or that their tools aren't as sharp as others' tools. Leading people into sharpening those tools, and believing that they can do this is quite a lot of what teaching programming comes out as. Give people the confidence to have a go, some pointers and feedback, and a few principles for the next round of exploration and pretty soon you have Kolb's learning cycle.

The other part of my answer was "Also good for using libs that are short on principles themselves." Maybe more than slightly flippant. But, occasionally, I use some library or tool, and find myself wondering how it works. An example gets me started. But straying from the one true path leads to failure. Sometimes there's a principle which has been broken or hidden or isn't the principle I was hoping for. Sometimes it's all a bit ad-hoc, a "tool" extracted from a single project that hasn't yet evolved principles and general applicability away from the use-case of the original project. Sometimes there's a principle, but it is unevenly applied.

I'll finish with an example: Java's ImageIO Input and Output streams and the standard Input and Output streams. Getting an Image written to an output stream and passed onto an input stream elsewhere, without specifying writing to a file, really could to be easier. The ImageInputStream and ImageOutputStream don't extend InputStream and OutputStreams, indeed an ImageOutputStream is also an ImageInputStream while the non-Image versions don't relate like this. *NIX is deep enough in my blood that I want pipes to do both input and output and just work, but in this problem the streams are in the same place and Java's PipedStreams need mucking about with threads if there's much data (and plugging a piped pair of streams into an ImageOutputStream without extra threads certainly broke for me). So, here's my example (arrived at by seeing several other examples, relating to my mental model of images and I/O, exploring, adjusting and combining):
  ByteArrayOutputStream os = new ByteArrayOutputStream();
  doRender(data, os); 
    /* method arranges the rendering object with the right parameters leading to
       ImageIO.write(theRaster, imageType, outStream);  */
  byte[] imgBytes = os.toByteArray(); // you know where you stand with an array
  ByteArrayInputStream is = new ByteArrayInputStream(imgBytes); // clunk
  docStore.putData(data.getName(), RENDER_MIME_TYPE, is, imgBytes.length);

This works - the rendered file ends up on my docStore (S3 in this case). But there's a part of me that would really rather not see that array. My images are (already) 60-80kB. I'm aware that at some point memory use may prompt me to revisit this (but lets not over-engineer it yet).

So, there you are: an example for you, which I wrote (today as it happens) by learning from a mix of examples and API documents (some of that learning happened years ago, of course). Whether the example is necessary because I misunderstood the principles of the libraries, or because the libraries haven't best applied available principles I'll leave to you to decide!

No comments:

Post a Comment