In the last post, I introduced the concepts of the module object, module, and package, concrete objects that exist within the Python runtime, as well as some basic ideas about packaging, finding, and loading.

In this post, I’ll go over the process of finding, what it means to find something, and what happens next.

A Clarifying point

I’ve been very careful to talk about finding vs. loading vs. listing in this series of posts. There’s a reason for that: in Python 2, the terms "Finder" and "Importer" were used interchangeably, leading to (at least on my part) massive confusion. In actual fact, finders, hooks, loaders, and listers are all individual objects, each with a single, unique method with a specific signature. The method name is different for each stage, so it is theoretically possible to define a single class that does all three for a given category of module object, and only in that case, I believe, should we talk about an "Importer."

In Python 2.6 and 2.7, the definitive Finder class is called pkgutil.ImpImporter, and the Loader is called pkgutil.ImpLoader; this was a source of much of my confusion. In Python 3, the term "Importer" is deprecated and "Finder" is used throughout importlib. I will be using "Finder" from now on.


When the import <fullname> command is called, a procedure is triggered. That procedure then:

  • attempts to find a corresponding python module
  • attempts to load that corresponding module into bytecode
  • Associates the bytecode with the name via sys.modules[fullname]
  • Exposes the bytecode to the calling scope.
  • Optionally: writes the bytecode to the filesystem for future use

Finding is the act of identifying a resource that corresponds to the import string and that can be compiled into a meaningful Python module. The import string is typically called the fullname.

Finding typically involves scanning a collection of resources against a collection of finders. Finding ends when finder A, given fullname B, reports that a corresponding module can be found in resource C, and that the resource can be loaded with loader D."


Finders come first, and MetaFinders come before all other kinds of finders.

Most finding is done in the context of sys.path; that is, Python’s primary means of organizing Python modules is to have them somewhere on the local filesystem. This makes sense. Sometimes, however, you want to get in front of that scan and impose your own logic: you want the root of an import string to mean something else. Maybe instead of directory.file, you want it to mean table.row.cell, or you want it to mean website.path.object, to take one terrifying example.

That’s what you do with a MetaFinder: A MetaFinder may choose to ignore the entire sys.path mechanism and do something that has nothing to do with the filesystem, or it may have its own filesystem notion completely separate from sys.path, or it may have its own take on what to do with sys.path.

A Finder (both MetaFinder and FileFinder) is any object with the following method:

[Loader|None] find_module([self|cls], fullname:string, path:[string|None])

The find_module method returns None if it cannot find a loader resource for the provided fullname. The path is optional; in the standard Python implementation, when the path is None it means “use `sys.path`”; when it’s set, it’s the path in which to look.

A MetaFinder is placed into the list sys.meta_path by whatever code needs the MetaFinder, and it persists for the duration of the runtime, unless it is later removed or replaced. Being a list, the search is ordered; first match wins. MetaFinders may be instantiated in any way the developer desires before being added into sys.meta_path.

PathHooks and PathFinders

PathHooks are how sys.path is scanned to determine the which Finder should be associated with a given directory path.

A PathHook is a function (or callable):

[Finder|None] <anonymous function>(path:string)

A PathHook takes a given directory path and, if the PathHook can identify a corresponding FileFinder for the modules in that directory path and return a constructed instance of that FileFinder, otherwise it returns None.

If no sys.meta_path finder returns a Loader, the full array of sys.paths ⨯ sys.path_hooks is compared until a PathHook says it can handle the path and the corresponding finder says it can handle the fullname. If no match happens, Python’s default FileFinder class is instantiated with the path.

This means that for each path in sys.paths, the list of sys.path_hooks is scanned; the first function to return an importer is handed responsibility for that path; if no function returns, the default FileFinder is returned; the default FileFinder returns only the default SourceFileLoader which (if you read to the end of part one) blocks our path toward heterogeneous packages.

PathHooks are placed into the list sys.path_hooks; like sys.meta_path, the list is ordered and first one wins.

The Takeaway

There’s some confusion over the difference between the two objects, so let’s clarify one last time.

Use a meta_finder (A Finder in sys.meta_path) when you want to redefine the meaning of the import string so it can search alternative paths that may have no reference to a filesystem path found in sys.path; an import string could be redefined as a location in an archive, an RDF triple of document/tag/content, or table/row_id/cell, or be interpreted as a URL to a remote resource.

Use a path_hook (A function in sys.path_hooks that returns a FileFinder) when you want to re-interpret the meaning of an import string that refers to a module object on or accessible by sys.path; PathHooks are important when you want to add directories to sys.path that contain something other than .py, .pyc/.pyo, and .so modules conforming to the Python ABI.

A MetaFinder is typically constructed when it is added to sys.meta_path; a PathHook instantiates a FileFinder when the PathHook function lays claim to the path. The developer instantiates a MetaFinder before adding it to sys.meta_path; it’s the PathHook function that instantiates a FileFinder.


Note that PathHooks are for paths containing something other than the traditional (and hard-coded) source file extensions. The purpose of a heterogeneous source file finder and loader is to enable finding in directories within sys.path that contain other source files syntaxes alongside those traditional sources. I need to eclipse (that is, get in front of) the default FileFinder with one that understands more suffixes than those listed in either imp.get_suffixes() (Python 2) or importlib._bootstrap.SOURCE_SUFFIXES (Python 3). I need one that will return the Python default loader if it encounters the Python default suffixes, but will invoke our own source file loader when encountering one of our suffixes.

We’ll talk about loading next.

A minor bug in the Hy programming language has led me down a rabbit hole of Python’s internals, and I seem to have absorbed an awful lot of Python’s import semantics. The main problem can best be described this way: In Python, you call the import function with a string; that string gets translated in some way into python code. So: what are the exact semantics of the python import command?

Over the next couple of posts, I’ll try to accurately describe what it means when you write:

import alpha.beta.gamma
from alpha import beta
from alpha.beta import gamma
from .delta import epsilon

In each case, python is attempting to resolve the collection of dotted names into a module object.

module object: A resource that is or can be compiled into a meaningful Python module. This resource could a file on a filesystem, a cell in a database, a remote web object, a stream of bytes in an object store, some content object in a compressed archive, or anything that can meaningfully be described as an array of bytes (Python 2) or characters (Python 3). It could even be dynamically generated!

module: The organizational unit of Python code. A namespace containing Python objects, including classes, functions, submodules, and immediately invoked code. Modules themselves may be collected into packages.

package: A python module which contains submodules or even subpackages. The most common packaging scheme is a directory folder; in this case the folder is a module if it contains an file, and it is a package if it contains other modules. The name of the package is the folder name; the name of a submodule would be foldername.submodule. This is called regular packaging. An alternative method (which I might cover later) is known as namespace packaging.

Python has a baroque but generally flexible mechanism for defining how the dotted name is turned into a module object, which it calls module finding, and for how that module object is turned into a code object within the current Python session, called module loading.

Python also has a means for module listing. Listing is usually done on a list of paths, using an appropriate means for finding (identifying) the contents at the end of a path as Python modules.

The technical definition of a package is a module with a __path__, a list of paths that contain submodules for the package. Subpackages get their own__path__. A package can therefore accommodate . and .. prefixes in submodules, indicating relative paths to sibling modules. A package can also and to its own __path__ collection to enable access to submodules elsewhere.


The problem I am trying to solve:

Python module listing depends upon a finder resolving a path to a container of modules, usually (but not necessarily) a package. The very last finder is the default one: after all alternatives provided by users have been exhausted, Python reverts to the default behavior of analyzing the filesystem, as one would expect. The default finder is hard-coded to use the Python builtin imp.get_suffixes() function, which in turn hard-codes the extensions recognized by the importer.

If one wants to supply alternative syntaxes for Python and have heterogenous packages (for examples, packages that contain some modules ending in .hy, and others .py, side-by-side)… well, that’s just not possible.


In the next post, I’ll discuss Python’s two different finder resolution mechanisms, the meta_path and the path_hook, and how they differ, and which one we’ll need to instantiate to solve the problem of heterogenous Python syntaxes. The actual solution will eventually involve eclipsing Python’s default source file handler with one that enables us to define new source file extensions at run-time, recognize the source file extensions, and supply the appropriate compilers for those source files, while falling back on the default behavior correctly when the extension is one Python can handle by itself.

My hope is that, once solved, this will further enable the development of Python alternative syntaxes. Folks bemoan the explosion of Javascript precompilers, but the truth is that it has in part led to a revival in industrial programming languages and a renaissance in programming language development in general.   Python, with its AST available and exposed at runtime, is eminently suitable as an alternative language research platform– except for this one little problem.

I am trying to solve that problem.


The weirdest interview I ever had…

Posted by Elf Sternberg as chat

The weirdest interview I ever had was shortly after I got laid off from Isilon. One-fifth of Isilon had been laid off in one brutal morning, and I was among them. The economy was deep into the recession, and like everyone else who had been laid off recently I had put out my resume and my shingle, then settled into my basement to teach myself everything I hadn’t learned in the previous eight years.

A large hardware-based American computer company whose name I won’t repeat, but which was once headed by a former US presidential contender infamous for an ad featuring demon sheep, reached out through its recruitment efforts and asked if I would come in for an interview. They said my skills were a perfect fit for a new product they had in mind.

The interview was way the hell out in Bellevue, hours and miles from my house, and utterly inconvenient for me. When I got there, I learned that their “new product” was basically a direct competitor for Isilon’s; they were trying to get into the mass storage market, and were having a hard time of it.

I was scheduled to be there all day. But as the first hour wound down, I already knew the job wasn’t for me. I didn’t really have the skills they wanted; I wasn’t all that familiar with the C/C++ side of the business, after all. I knew more about the Isilon product from the configuration side than anyone outside of field support, but not much about the internals. More than that, the job didn’t sound interesting. Java and Swing weren’t technologies I was particularly interested in. The office environment seemed cold and unfriendly. The commute was murderous. And I’d just gotten cut from an infrastructure job; why would I want another one?

When the second interviewer came in, I said to him, “I’m sorry, but this job isn’t really for me. I think we should call it here.”

He looked at me, confused. “You mean you just want to… stop?”

I said, “I’m not going to take the job.”

“But…” He stopped, utterly still, for a few moments, then rose and left. A few minutes later, he came back and said, “The candidate isn’t allowed to end the interview process.”

I told him flatly, “I don’t think you can legally stop me from leaving the building.”

He stopped for a moment and looked very confused.  “No, that’s true,” he said. “Okay, I guess I’ll, um, escort you out.”

“Thank you.”

To this day, I have no idea what he meant by “The candidate isn’t allowed to end the interview process.”  I suspect that what he got from the higher ups he went to talk to was “There’s no process for the candidate ending the interview,” and went from there, but… weird.


Making any language loadable in Python

Posted by Elf Sternberg as Lisp, python

I may have inadvertently contributed to the Coffeescriptification of Python. And in doing so, I have learned way too much about Python internals.

Coffeescript probably wasn’t the first programming language that transpiled to Javascript, but it was the first that caught on. Coffeescript and all its successors, like Clojurescript, Gorilla, and Earl Grey, exist because Javascript is a terrible language written on top of a truly universal runtime: the browser. Then some bright person took that runtime and put it on the server and called it Node, and then someone else took it and put it in the database and called it PLv8, and folks who’d been battle-hardened living in the browser could now be productive on the full stack. Transpilers exists because, rather than write in that terrible language, they allow people to write code in a language they like and it ends up all running in Javascript in the end.

Python doesn’t have a universe like this. Because Python is actually a fairly good dynamic language, and the syntax is pretty much the best of the pick of its competition from Perl, Ruby, Visual Basic, or obscurities like Pike or Io. But the Python runtime is not much loved; the global interpreter lock constrains Python’s performance in many ways.

There are a few, though, including:

  • Hy, a Lisp
  • Mochi, a Python derivative with Erlang actors
  • Dogelang, an ML-imitating… thing

With the coming Gilectomy, Python may finally move past some of its more egregious runtime limitations, and when it does, maybe alternative language developer will start to look at it more seriously, and develop languages that run on top of it the way Pure or Elm run on top of Javascript, yet manage to do so while honoring functional purity in a very real way.

But Python has one other limitation: the extension “.py” is hard coded into the import library that ships with python. Hy had a fairly good work-around that let it load its libraries and python side-by-side, and Doge seems to have a similar solution, but none of them were universal.

Then I learned that Hy doesn’t work with Django.

This all started out because I wanted to write a Django app. It had been awhile, and I had a specific desire to take what I’d learned writing mp_suggest and do something a little more complex with it, as a way of organizing the several thousands of CDs and their rips I have on my home NAS server.

I’m sure there are Django apps out there that already do this, but I wanted to write my own, and as you’ll see if you follow that like mp_suggest runs on Hy. Most of Django worked, but custom commands couldn’t be found by a Hy-based version of manage.hy, which annoyed me.

An itch that needed scratching! It took me two weeks; I probably put about 20 hours into untangling all of it. The real issue was that Hy was hooking into Python’s sys.meta_path, when what was needed was something even earlier in the import chain, a sys.path_hook. Path hooks are black magic. I’ll have a more technical entry up in a little bit.

In the meantime, here’s the patch. At first, it was just hooking up the path_hook, but as I was looking at the code, I realized that the hy-specific parts were actually fairly limited. It came down to two items: filename extensions, and a compiler. Everything else was just… boilerplate.

So I abstracted it out.

It’s now possible, by adding a few extra imports to your file, to build a Django app where:

  • Your views are written in Hy
  • Your models are written in Mochi
  • Your custom commands are written in Doge

And it all just works.

There have always been attitudes that I don’t understand. James Hague’s recent Death of a Language Dilettante is one of them. Now, most people know that I’m a language dilettante; that I love everything new and shiny coming out of the programming language space, even if it does seem that in some ways that space has been tapped out, with languages becoming ever more blunt on one side with Go and Python, and ever more esoteric on the other with Agda and Idris.

James issues a challenge: “Give a language with a poor reputation (JavaScript, Perl) to someone who knows it passably well and–this is the key–has a strong work ethic. Let the language dilettante use whatever he or she wants, something with the best type system, hygenic macros, you name it. Give them both a real-world task to accomplish. My money is on the first person by a wide margin.”

Well, sure. Tooling matters. Experience matters. In the ${DAY_JOB} I write a ton of Javascript, and I write it more than passably well. I also write a ton of Python, again more than passably well. Those are two languages at which I consider myself working at an expert level.

On the other hand, I also love esoteric languages and, more importantly, every time I’ve worked in one, it’s made me a better programmer in the languages I use professionally. Learning Haskell made me really appreciate your responsibility for separating TheWorld from the program, and taught me the power of higher-order functions; learning Lisp made me really appreciate the power of expressions qua expressions in a way Haskell tried hard to make so simple you didn’t notice them. When you work in a “real world” language, those matters are present in everyday details, and knowing how to work with them is a gift, not a disease.

Right now, my “esoteric” language is Hy. Hy is an implementation of Lisp that produces a Python AST and runs on the Python VM. Hy implements its Python importlib layer incorrectly, and in a way that means certain Django features are unavailable to Hy developers, and I intend to fix it.

Unfortunately, Python’s importlib is pretty esoteric in its own right, and implementing a new pather/finder/loader has been a beast. But I’m closing in on a solution, and I intend to have it working soon.

And then I can do what I set out to do, which is write a Django app. Only I can do it in Hy.

And along the way, maybe get a new Django command, newhyproject, out of it, as well as newhyapp and maybe newhymigrations. But those are stretch goals. Right now, all I want is for Hy to interoperate with Django correctly, and then I can think about future projects.

So I’ll take James’s challenge, but I’ll have to say: “Both is good.”

There’s a comment I get from my peers from time to time that boils down to: “Your code is tight and expressive, and the names are well-chosen, but it’s ugly and it makes reviewers uncomfortable.” And that’s when I realized that functional programming, by its very nature, causes us to write visually unattractive code.

The human eye likes rhythm. Aesthetically pleasing visual arrangements often involve creating a sense of flow, a sense of time, and a sense of transition from one scene to the next. Repetition is a key element of beauty. Artists and musicians come up with motifs and set up the expectation of repetition, and then maintain, modify, or diverge from the repetition into a new landscape where a new repetitious motif must be established and maintained in order to sustain the impression of beauty.

This is utterly in contrast to DRY: Don’t Repeat Yourself.

Well-constructed, highly functional source code written in a traditional enterprise language like Javascript, Python or C++ has very little repetition. It’s not supposed to. The comforting rhythm of a long if/else tree is broken out into a lookup table. The comforting rhythm of long transformations is itself transformed into a brutally compact map/reduce. As we try to make the syntax of the language itself more succinct (the Javascript keyword function has been brutally shortened to =>, for one very recent example), familiar “hooks” onto which the eye might rest and understand become harder to find.

It’s not all this bad. Well-written Haskell is actually very pretty to look at, but it’s also daunting to realize that every word on the page is dense with meaning.  It’s also significant that Haskell’s organization within a source file isn’t sequential; the compiler hooks up all the definitions in the correct order regardless of their location.  This gives the developer greater freedom to organize code into meaningful units, but it can also mean that understanding the file isn’t as simple as reading it in order from top to bottom.

That sense of intimidation is even greater when you apply it to more traditional languages that can support a functional style. A language like Javascript can support strong functional idioms, but in doing so, you end up writing the code equivalent of Shostakovitch and the “difficult listening hour.”


Analysis Paralysis

Posted by Elf Sternberg as chat, personal, programming

There was an article highlighted in a variety of nerdy news sites called “I’m A Good Engineer But I Suck At Building Stuff,” and while I was reading it, I felt the writer Lionel Barrow’s pain.

Because I’m a better engineer, but I still suck at building (some) stuff. When Barrow writes: “I find myself instantly criticizing my technique, to the point of paralysis. This function is hard to test; this object’s dependencies need to be injected rather than initialized internally; that module needs an integration test; and so on and so forth,” I don’t sympathize. Not only do I know how to do all those things, I know how to avoid them. A hard-to-test function should be broken down into public components; injected dependencies shouldn’t be a difficulty, they should absolutely be the starting point of your composition; integration tests are mastered by giving your interface the smallest possible surface area. When Barrow writes, “Even when writing spike or proof of concept code, I find myself revisiting the same lines over and over again, looking for the best, most natural expression of the ideas it contains — obsessing over my own construct, rather than on the thing my code does,” I remind my self that the very first thing my code does is communicate with other developers. Code is meant to be read first and compiled second. Yeah, maybe if you’re doing a prototype you can cut that corner once or twice, but get it out of your habit.

Where I work, my fellow engineers have a joke about “Elf’s de-cleverizing pass.” I work in Javascript but am a Lisp programmer at heart, and I start by writing everything in terms of transformers and partials wrapped inside and around map/filter/reduce, and then at the end I will remove the partials and break out the transformers into functions and make the code look like something my peers expect. Most modules I write end up as long, easily-followable lists of var expressions that end with a single side effect, because that’s what all code is: an expression that ultimately has some real-word consequence.

On the other hand, I feel Barrow’s pain because I, too, suck at building stuff. Modifying other people’s code is easy. Even if I’ve been tasked with building “something entirely new” for my employer, the skeleton of every application we’ve ever built is in our repository, and if I can find something that does 51% of what my new task calls for, the rest is more or less fait accompli.

But when it comes to my own projects, I get analysis paralysis. I wanted to write a simple compiler with a few ideas of my own, something that would make my life as a developer easier, at least for a few follow-on projects, and …

There’s way too much to know about compilers. Stephen Diehl’s wonderful (if woefully incomplete) articles on “Write You A Haskell,” James Longster’s expedition into his Outlet project, all the effort I put into Lisp In Small Pieces, parsing, hygiene and scope, type systems (oy! Type systems!), compiling with continuations… My eyes start to glaze over and I get this terror that I’m never going to be smart enough or have time enough to do anything at all. The size of my ambitions overwhelms me. I lose track of what I’m trying to do as yet another article on CPS Transforms and Nanopass Compilation comes to my attention.

The twin impulses of wanting something useful now and wanting to know everything now are, rather than helping each other, constantly at war with time itself. I’m sure it’s an ADHD problem that no amount of Adderall is going to handle, because it’s not about attention, it’s about desire. I never have this problem professionally, because when it comes to money both what must be done and what I desire to be done are more or less well-aligned. It’s about the projects I’m really passionate about; I don’t want to screw them up, and there’s no pressure to get them out the door. I really don’t know how to make these impulses play well together.

While I can’t comment too strongly on the performance aspects of this article on Green Thread performance, there’s a paragraph at the beginning that caught my attention:

If one looks closely enough at green threads, such as Go’s ‘go’ statement, it’s not hard to realise that they are really a flow control mechanism similar to ‘if’, ‘for’ or ‘while’ statements. Where ‘if’ allows you to skip a block of code, green threads give you a way to easily switch between different points of execution. Very much like ‘if’ or ‘while’ they are glorified jump statements.

To which my initial reaction was, “Yes, that was covered in Chapter 3 of Lisp In Small Pieces.”

In fact, everything you need to know about control flow is covered in Chapter 3 of Christian Queinnec’s amazing, now 20-year-old book. In terms of control flow mechanisms for programs, we haven’t invented anything new since that book came out. If you’ve read Lisp in Small Pieces and Why Functional Programming Matters, you’ve read everything you need to create modern, composable, performant software. The biggest insight in LiSP is that all of these mechanisms for code parallelization, threads, communication modes, concurrency, and so forth are exactly the same as “if”; everything else is (important) details, like performance, but the underlying nature of programming is unchanged: there is one and only one instruction that matters: conditional jump. All else is commentary.

Necessary commentary. Abstractions necessary to constraining the development process in order to eliminate error. But still, commentary; when someone expresses “surprise” that Go’s go is semantically similar to if, it tells me that they’ve never taken, or somehow missed, the fundamentals taught in their CS program.


Javascript Misunderstandings

Posted by Elf Sternberg as Uncategorized

In a recent post, Zisis Maras recently talked about some interesting work with overriding the prototypes for Storage/localStorage to provide key/value space for different sessions running in the same browser. For the longest time, the advice was “Don’t ever overrride the native functions or methods,” but a lot of people seem to be ignoring that advice in the post-IE 11/Chromium/Firefox era.

But one thing he said bugged me. He talked about “cloning” functions, and then said his “favorite trick” for it was _func = func.bind({}).

This is such a terrible description (and comment) that it needs a clarification. Javascript’s context management is fundamentally sound: it’s call-by-reference with strong closure guarantees. It has one problem, though: the this special variable.

When the Javascript interpreter encounters the function keyword, it builds a new function. Variables within the function can be bound, which means that they’re meaningful only within the scope of the function, and free, in which case they were meaningful when the function definition was encountered. When the function is called, those free variable retain the value they had at the moment the function was constructed1. We call the free variables the function had at its construction its context.

When Javascript was new and poorly implemented, the this variable was introduced. It was meant to provide additional context in browser-based environments; specifically, it addressed how event information was made available to the HTML-3 “onclick” attributes, which pointed to a function named in a string argument, as strings are the only thing you’re allowed in HTML. The this variable was then redefined by successive Javascript versions to describe a number of different features, including (a) the event context, (b) the global environment, (c) a local environment to which it is bound (this word is important, remember it) with the new operator. The last is how Javascript does object oriented programming.

When a function is called with new and if the object it returns has functions bound to a .prototype attribute, it is called a constructor and the bound functions are called methods, and when that object.method() call happens, .method() gets its parent object in its this variable. (If the constructor doesn’t return an object, this is returned automatically.)

In callbacks and other asynchronous operations, this can get lost. It’s not unusual in modern Javascript to define an object that has asynchronous needs (fetch data, write data, listen to DOM objects), and an asynchronous handler within the object will need access to two different instances of this: The event’s context, and the object the event is meant to modify.

Traditionally, we used to write var that = this; before the callback was defined, making that a free instance of the object, so inside the callback that became our handle to the object, while this remained the handle on the event context.

In Javascript 5, the Function.bind() function takes a function and an object, and returns a function in whith this is locked down so that if the interpreter attempts to provide an alternative within that function, it’s prevented from doing so. Since most modern event handler protocols pass the event context in as an argument to the event handler, we no longer need the event context this, and .bind() exists to make it go away (while still supporting applications written in really ancient Javascript).

This is a long-winded way of saying you never “clone” a function in Javascript, the way one might deep copy an array or an object. Instead, all you’re doing is assuring that the function has a set and unchanging1 instance of this.

Understanding this is, depending upon your point of view, either absolutely essential (as it is in my day job) or absolutely unnecessary (as it is in my personal software projects, where I use closures and hidden contexts instead). I would go with essential if you want to write Javascript professionally. But Maras’ contribution is unfortunately only going to make that understanding harder.

1This is strictly true, but since Javascript is call-by-reference, this can lead to surprises for new developers. If a free variable was an array or an object (and objects are always objects), if the contents of the object or array are changed outside the function, they’re changed inside the function as well! The free variable refers to the object or the array, and not their contents. When we say “unchanging,” we mean that the thing referring to is unchanged; not the thing referred to.

I have less than ten hours to teach a dozen surly high schoolers to go from opening their notepads to writing Tetris. This is the objective: to give them enough HTML, CSS, and Javascript to be able to write a very simple video game.

Here’s what I told them:

A high school programming class and a high school cooking class are more or less the same. At the end of a cooking elective, if you followed all the steps you were required in class, you go home with two things: a cake, and a recipe for cake. All of you are going to eat the cake. Some of you might try to make the cake a second time. A few of you will wonder, “If I change this chocolate to strawberry, and make the cake again, will it work?” And one of you might go on to be a professional baker.

The same thing is true of this class. At the end of a class, you’re going home with a game written in HTML, and the source code to that game. The game will be fully playable. You’ll even have the source code, so you could try transferring the game to other computers to see how it works there. A few of you will look at it and wonder, “If I re-arrange these things, can I turn Tetris into Pac-Man?” (The answer is “Yes,” by the way.) And one of you might go on to be a professional software developer.

Oddly, the teacher who organized the elective tells me that that’s the best and most succinct description of what an elective like this is trying to accomplish. So I guess I’m doing something right.

Of course, there’s already that one guy who has a three-d game written in Unity, and took this class so he could learn how to put up a website about it…


July 2016
« Jun