Tao of the Machine

Programming, Python, my projects, card games, books, music, Zoids, bettas, manga, cool stuff, and whatever comes to mind.

Good design requires a dictator

Paul Graham: Design and research. No anti-Python rhetoric this time, but an interesting look on (language) design. Written from a Lisp-y point of view, of course, but much (if not all) applies to Python as well.

For example, interactive interpreter and dynamic languages:

"For example, it is a huge win in developing software to have an interactive toplevel, what in Lisp is called a read-eval-print loop. And when you have one this has real effects on the design of the language. It would not work well for a language where you have to declare variables before using them, for example. When you're just typing expressions into the toplevel, you want to be able to set x to some value and then start doing things to x. You don't want to have to declare the type of x first. You may dispute either of the premises, but if a language has to have a toplevel to be convenient, and mandatory type declarations are incompatible with a toplevel, then no language that makes type declarations mandatory could be convenient to program in."
"Whatever the story is in the sciences, true collaboration seems to be vanishingly rare in the arts. Design by committee is a synonym for bad design. Why is that so? Is there some way to beat this limitation?

I'm inclined to think there isn't-- that good design requires a dictator. One reason is that good design has to be all of a piece. Design is not just for humans, but for individual humans. If a design represents an idea that fits in one person's head, then the idea will fit in the user's head too."

Posted by Hans Nowak on 2003-01-03 15:12:32   {link}
Categories: programming

Yesterday's roundup

Besides hacking Kaa, I did some more stuff yesterday.
  • Python 2.3a1 builds right out of the box on the Mac. Well, a configure / make install does, at least. I haven't tested a framework build; heck, at this moment, I don't even know what a framework build *is*! <0.1 wink> So that is for later. Probably when I've digested large parts of Mac OS X Unleashed...
  • Charm works, well, like a charm. I used it to work on Kaa. While not perfect, it is quite useable. Maybe I should release it someday, although I don't think I should do that until all Requirements (20 at the moment) are met. (We're at 14/20, currently.)
  • As for Kaa, I also fixed a coupla bugs, and added two new buttons: "code" (inserts <code><pre> tags) and "blockquote".
  • Idea: What if Kaa also could validate HTML? That could be an interesting feature.
Gotta get back to work. More hacking later... maybe.

Posted by Hans Nowak on 2003-01-03 10:29:41   {link}
Categories: switch, Python, Kaa

0.9 coming up soon

Some good late-night hacking today. I figured it would be nice if Kaa (finally, some would say) generated an RSS file, so I added that. It may still contain bugs, or not validate, etc; I will take care of that later. Important is that most of the functionality is there, and will be included in Kaa 0.9, which should not be far away now.

In the navigation bar on the right, there should be a new entry "RSS" now. Whether the RSS file shows up correctly is a different issue, but it is there. :-)

More later. I need some sleep...

1:56 AM. The RSS Kaa generates validates! Yay! :-)

Posted by Hans Nowak on 2003-01-03 01:27:37   {link}
Categories: Kaa

The import hooks oddity explained

See yesterday's post. Just van Rossum writes:
You may have to do sys.path_importer_cache.clear() to make sure no handler is registered for the path (this is most likely a None value, signalling it should use the builtin mechanism). Check PEP 302 for the details. You should then get a "Wahey!" for each member of sys.path (and as a bonus no file system import will work after you installed your hook ;-)
Basically, this is what happens:
  1. For every directory in sys.path, an entry in sys.path_importer_cache is created, which defaults to None (unless there happens to be a valid importer for that path, etc).
  2. On Windows, everything in sys.path has a matching entry in sys.path_importer_cache. After installing the new hook, the import statement traverses all the paths, finds None, and doesn't call the new hook. That's why there is no output.
  3. On Cygwin, for some reason the path '' isn't included in sys.path_importer_cache. (Apparently this is also the case for other systems... probably Unixoids... I'll have to try this on the Mac.) The hook is called for path '', that's why we see one "Wahey!".
  4. If you run the script with python -S (omit site.py), then the cache is empty, and you get to see a whole slew of paths being tried. Ditto if you put the sys.path_importer_cache.clear() in your program before you set the new import hook.
So, it's not a bug. It's a feature that has a surprising side effect if you don't know exactly what's going on. It makes sense, when you are aware of the cache and what it does (esp. a None value). The "naive" approach didn't work too well though.

In fact, Windows may show the correct behavior, while the other systems don't -- after all, their sys.path isn't in sync with sys.path_importer_cache. Maybe because of site.py?

Update (6:15 PM). Just also writes:

There are two idioms for adding path hooks:
  1. sys.path_hooks.append(myhook)
  2. sys.path_hooks.append(myhook)
    sys.path_importer_cache.clear() # take over existing path items
I'll remember to use these. Neither the PEP nor the section in WNIP2.3 mentions this.

Posted by Hans Nowak on 2003-01-02 14:24:03   {link}
Categories: Python

2.3's import hooks

2.3a1 arrived yesterday, and contrary to what I normally do, I downloaded and installed it even though it's an alpha version. I wanted to play with the new import hooks.

My first test program, using sys.path_hooks, didn't work at all. I added a function to it, but that function wasn't even called upon importing. I'm probably misunderstanding and/or doing something wrong.

Then I tried using meta_path. This worked better. I've had an idea for a while, to import modules over HTTP, and figured I could use the new hooks for that. (The idea may not be new, but it's just something I've been wanting to test for a while.) My conclusion is: you can, but you're probably better off writing a custom function (say, import_http) for that and use it when you know you want to import a module that's on the Net somewhere. Like

mymodule = import_http(url, "mymodule")
or something like that.

You can, however, write an importer (or "finder", really) and a loader for that (see What's new in Python 2.3 and PEP 302). The finder (which should be passed a base URL) can check if the URL exists, and if so, return it, or a file-like object (e.g. from urllib or httplib), otherwise it should return None. The loader can then load the code, create the module object and return it.

Problem: You don't want to do this for every module. Added such code to meta_path would be called for every import, including built-in modules and standard library. Connecting to the Net every time seems a bit... inefficient.

So what if we define a pseudo-package, say "http", to indicate a module can be found online? Like

import http.foo
which looks for module 'foo' online. The import hook mechanism sees this, correctly, as a package http with a module or package foo inside. This means the finder will see 'http' first. I'm sure some trickery is possible to create a pseudo-package and to prevent our finder and loader from importing http (and to import foo instead), but it seems far-fetched to me right now. So I didn't try that.

Adding a "magic" prefix, like http_, is possible, but seems ugly and un-Pythonic. The finder can check if its module name starts with "http_", and continue if that is the case, return None otherwise. import http_foo creates a module http_foo though, not foo, which seems less than elegant.

Konkludo: Let's not use import hooks for this. They were most likely not meant for this anyway. :-) Using a custom function still seems the best solution.

What still puzzles me, is, why doesn't this code work?

import sys

def f(path):
    print "Wahey!"
    return None


import nothing  # needless to say, doesn't exist
This raises an ImportError, as expected, but doesn't print anything besides that. Function f is not called. Judging from the explanation, it should be called, really. I must be doing something wrong...

Update. The PEP and the "What's new" section contradict each other, by the way. PEP:

sys.path_hooks is a list of callables, which will be checked in sequence to determine if they can handle a given path item. The callable is called with one argument, the path item. The callable must raise ImportError if it is unable to handle the path item, and return an importer object if it can handle the path item. The callable is typically the class of the import hook, and hence the class __init__ method is called. (This is also the reason why it should raise ImportError: an __init__ method can't return anything.
vs "What's new in Python 2.3":
sys.path_hooks is a list of functions. Each function takes a string containing a path and returns either None or an importer object that will handle imports from this path.
Another update, 10:08 PM. I built Python with Cygwin (runs right out of the box, by the way... nice), and running the code with "Cygwin-Python" does work as expected. In other words, with the Cygwin build the function is called, and with the vanilla Windows build is it not. Very strange. I posted to the newsgroup, let's see what the experts say about this. If it's not a bug, then it's at least confusing behavior.

On a related note, I also may have found a buglet in the documentation. Hmmz...

Posted by Hans Nowak on 2003-01-01 14:23:24   {link}
Categories: Python

30 cards

Apparently, Magic the Gathering can be played just as easily with 30 cards. (The normal deck size is around 60.) We played a game with 30-card decks yesterday; this seems a good format for casual play. Some thoughts:

1. This format is meant for casual play.

2. There should be a rule about what happens when you reach the end of your deck. My first thought was that when that happens, you just look at the life totals, and the player with the highest total wins. This can be abused though; e.g. millstone decks run through a 30-card deck much faster than through 60. Also, including a lot of cantrips and other "draw a card" effects can give a player an unfair advantage. Then again, as said, this format is meant for casual play. If you are abusing the rules because you really must win, maybe this casual format isn't for you. :-)

3. There should be a maximum of 2 of the same cards. 4 is just too much, since normally you will have something like 10 lands + 20 other cards. Personally, I think it wouldn't hurt to make those 20 cards all different.

4. New forms of deck building. Wade through your commons, pick cards that generally look OK, or try using a coherent theme (e.g. a goblin deck, still possible in 30-card format). A Falcon deck becomes possible as well (don't forget to include Soraya the Falconer). Etc, etc.

5. Playing is more important than deck building. This roughly translates to, having fun is more important than winning. Don't enter the place with a well-oiled tournament deck; rather, use some interesting cards that will delight friend and foe.

And shuffle well, so you won't get 8 Forests in a row like I had... ;-)

Posted by Hans Nowak on 2002-12-29 11:19:35   {link}
Categories: CCGs

Generated by Firedrop2.