|
My brain is built of paths and slides and ladders and lasers and I have invited all of you to enter its pavilion. My brain, as you enter, will smell of tangerines and brand-new running shoes.
| Thursday, December 29, 2005 |
 Hapi Djus |
Posted - 12/29/2005 12:16:34 AM | I think I might keep my flatmate. Lately he seems to be starting this trend of actually having some significant value, aside from being an efficient resource sink for my Moon Pies and other valuable edible commodities.
You see, he has concocted a drink. Anyone can invent a drink, but this one is really something. The inspiration came as we drained our reserves of beverages to make way for our upcoming New Years fare; this involved disposing of a decent amount of gin. Somehow, a couple of bottles of Bawls got mixed up in this ruckus as well, and being generally bored and lacking sleep, we decided to try mixing them.
After some testing, we have arrived at my proposed final mixture, which I present to you now:
Bawls on Gin
Ingredients:
- 1 bottle Bawls
- 1 shot gin
Open the Bawls and drink off the first couple of swallows; this should leave the level of the Bawls just at the bottom of the neck where the bottle begins to flare out. Pour in the gin and mix gently with a circular motion. Consume and enjoy.
| |
| Tuesday, December 20, 2005 |
 Awwwwww |
Posted - 12/20/2005 3:39:06 PM | Ladies and gentlemen, please welcome my fourth and most recent nephew, Chase Andrew. All seven pounds, one ounce of Chase got permanently stuck with my weird and goofy family at about 7 AM on December 17th.

His big brother Chad is currently in a state of quantum flux, not entirely sure of whether to be jealous or overprotective. It's actually kind of funny to watch.

And with that, I'm going to sneak off and go on Christmas vacation, so I can go bug my other two nephews. To top off my devious nastiness, I'm going to leave everybody hanging about that article that I threatened you with.
Tune in next week for more total inanity! Woot.
| |
| Monday, December 19, 2005 |
 Delusions of Writing Ability |
Posted - 12/19/2005 10:01:24 AM | So I've been reading the forums instead of working on my own time, and I've started to see a lot of stuff cropping up that has to deal with abstraction and programming languages. I have a feeling that this is really not much different than usual, but now that I'm thinking about it, I'm seeing a lot more things that can be interpreted from that perspective. (Funnily enough, I have that pattern in all kinds of things. Ever learn a new word, and suddenly hear it in, say, twenty places all in the same day? I suspect this is a well-documented psychological phenomenon, but being the unedumacationed type, I have no idea. Anyone know if this has a name?)
Anyways, all of this has got me thinking. Specifically, it's got me thinking about articles. I love reading articles. Articles are nice and short, but still have goodies - well, the good ones do. Articles are perfect. You can sneak them in while you wait for the complete project rebuild to finish, and if you're questioned, a lot of articles can be linked to your work. ("I'm reading up on good design principles. It'll help us deliver this project faster and with fewer bugs. Really.") Since I like articles so much, and since I'm a veritable sack of hot wind about abstraction levels lately, I've come up with an idea: I could write an article.
Actually, I really don't care about writing articles, I just want something I can point people to about abstraction (and programming languages) without having to write all of my thoughts out from scratch. Besides, a long forum post is just mind-bendingly dull. Nobody reads long posts (ask Wavinator). Take the exact same text, slap a byline and caption on it, and shove it off into its own page, and it becomes great literature.
So, as usual, I've expelled a lot of excess gibberish to get to the point I'm after: I'm contemplating writing an article. That sounds distinctly like work, however, so I've decided that I'll only bother with it if there is sufficient demand. I know someone at least glances in here from time to time (either that or I've got a loyal fan who just sits in here pressing F5 all day), so here's my challenge to you: post your demand, and I'll give you an article.
| |
| Wednesday, December 14, 2005 |
 RED ALERT! Brain dump at twelve o'clock! |
Posted - 12/14/2005 11:07:58 PM | It is time yet again to dribble out a bunch of half-finished thoughts about things I don't really properly understand. For this episode, my topic of choice is code integrity.
First of all, let me buy myself some time by defining the phrase "code integrity" as I intend to use it. It might be worth noting that I just completely made that up, so it's probably a stupid choice of words. That aside, however, I think the point I was originally trying to convey (before I got sidetracked in thinking up all of this self-referential gibberish) is that code can be broken. Deeply profound, I know.
So code breaks. The first important question is when did it break: was it already broken because the design itself was broken? was it broken because it wasn't implemented correctly? did it break during a maintenance change? did it break due to coupling to another piece of code (or data) which has changed? did the evil Nazi code gnomes sneak into the production system in the middle of the night and spread havoc?
Once we know when the code broke, we need to know something else: how the breakage was found. An excellent piece of advice (which, as usual, I stole from The Pragmatic Programmer) is to make sure that things fail as quickly as possible. A good example of this is division by zero. On a modern IA-32 processor, a division by zero actually throws an exception at the hardware level. This is a great idea. Suppose the processor didn't do anything, and simply expected the user to be smart enough never to divide by zero. Some random data could get stuck into memory, and cause all manner of bogus results.
Email is an example of how not to implement failure. In the common POP3/SMTP email structure, I send an email to this_address_is_bogus@bogosityltd.com. Clearly, it never arrives. However, I don't know that this email failed until several hours later when the "I gave up trying to deliver your message" reply comes back from whatever server ended up barfing on my fake mail. If I have a spam filtration system, or if servers simply aren't configured to relay those messages, I may never know that my vitally important secrets about saving the world from Doomsday never reached my good friend Mr. Bogus.
In software, we've got some nifty things like assertions exceptions that let us make sure our code dies when things go wrong. Wait, wait, wait, though... aren't crashes bad? We don't want our users to see "ASSERTION FAILED IN FOO.CPP" when they run our program, right? Most likely, the answer is that no, we do not in fact want users to see that.
This brings up an important question. We want to fail as soon as possible, so that damage doesn't propagate into other areas of the code. We don't want to put an ugly "illegal operation" or "assertion failed" or "unhandled exception" type message on the screen. However, we also want to make sure that the information about the failure is preserved, so that the failure can be diagnosed and repaired.
There are some tricks we can use for that, like using structured exception handling hooks to capture OS and hardware exceptions (for C++ and Windows apps), logging systems, automated phone-home error reporting mechanisms, etc. However, those are beyond the scope of my rambling, by which I mean to say if I get started talking about that, I'll forget what it was I was pretending to talk about in the first place.
So, let's gloss over that issue for a bit. We now have a failure, and information about it. Assuming we have a good programmer handy, we also can fix the failure. We pat our trusty pager that got us out of bed at 3 AM for the fifth time this month, stumble out to the car, and drive home... oh wait, I have to be back at work in an hour... crap. One of those goofy proverb-things that your grandmother used to love to say comes back to mind... something about prevention and cure...
This is where it gets interesting. In fact, some very smart and capable people have already answered the question, a long time ago. You can tell that these people are smart, because they've answered the question, and I haven't even said what the question is yet. Deep Thought would be proud. The question is, can't we do something to keep these failures from happening at 3 AM and getting me out of bed?
The answer, of course, is fourty two yes. The smart ones who came before us (well, before me anyways) have done some hard work and hard thinking, and invented good stuff like unit tests. We can use unit testing to find out when things fail, and how exactly they fail. Best of all, unit tests can keep failures from ever making it into production. They're like little magic pills that make bugs disappear, and silence your pager (at least at 3 AM).
But unit tests are work, and I'm a lazy bastard. I hate work. Work is... well, work. It takes effort, and all of that stuff, and I just can't be bothered. Actually, I personally do run testing as much as possible, but only in a sort of lazy way. That's really why I'm dumping all of this; I think it should be possible to have unit testing that isn't work. The usual argument goes that the time spent writing and running a testing suite is easily won back in time not spent getting woken up by your pager at 3 AM. However, I think this is silly. I think I should be able to stay in bed at 3 AM, without taking the battery out of my pager, and without doing extra work making unit tests.
Here's the theory. First, we take our level-of-abstraction metacode thingy, and we generate code with it. Then, we use the same system to generate tests. This is based on a very arcane and deeply mysterious principle, which I shall now reveal to you, as Apoch's First Theorem of Testing Software:
Unit tests exist at a level of abstraction that is slightly higher than the module which the tests are designed to test. Therefore, both the unit test and the interface for the module itself can be specified completely at a level of abstraction that is slightly higher than that of the unit test itself.
The upshot of this is that it's getting late, my fingers are cold, and I want a sandwich. Sandwiches are in terribly short supply at the office. (Why I'm here doesn't really matter, although thankfully it does not involve a pager, and it doesn't include being awake at 3 AM. Yet. Sammidge.)
Oh, sorry, that's not the upshot of this. The real upshot of this is that unit tests can be generated with the same knowledge that generates the abstraction code. (This might not actually be an automated process. In fact, at the moment, it rarely is; the knowledge is stored in some people's brains, and the generation is done by typing and thinking.)
What's great about this is that, if we know enough to build a unit test, we also know enough to build a layer of abstraction. This means that the knowledge needed to generate both is highly coincident, if not identical. Let's call the set of knowledge needed to build the unit tests K(u), and the set of knowledge needed to build the abstraction layer K(a). Now, we'll introduce several contrived variables, and spend the rest of the discussion trying to make naughty words with our symbolic names.
I really should stop doing this late at night... I'm having a little trouble with that focus thing. Sammidge.
Now, for the sake of argument, let's say that the total knowledge about a module is K(u) U K(a) U K(o) where K(o) is other incidental knowledge about the module that isn't covered in the creation of the unit tests or the abstraction layer. I think that the K(o) set will always be empty, but I don't know for sure, so I'll leave it in there for now. Let's call all of this knowledge K(m).
Given some module m, and the specs for that module, K(m), we can therefore generate both an abstraction layer and a unit test for m. This should not be surprising. In fact, it's what software design is all about. However, the good stuff comes next.
It is quite likely, although I'm too lazy to logically prove, that there exists some common medium of representing K(m) that allows a nontrivial portion of the code for m and m's unit tests to be generated automatically. I hypothesize that the complete interface for m can be generated, and almost the complete unit test code for m. Actual implementation of m, and any dependencies of m that must play a role in the unit test, will probably have to be handled manually, but we're still ahead of the game.
We already need a medium to represent K(a) so we can build the abstraction layers automatically. Clearly, if we choose our medium wisely, we can use the same medium to generate quite a bit of code, all from a high level of abstraction. Our whole goal here is to work as abstractly as possible; by doing some mental trickery, we can actually work at a level of abstraction that is more abstract than the level of abstraction that we are trying to create.
I think there's probably some more that could be said, but that last bit really cooked my brain. Speaking of cooked brain, I'm really hungry. Grarghhh.
| |
| Saturday, December 10, 2005 |
 3H-GDC m.IV |
Posted - 12/10/2005 2:18:29 PM | Well, the submissions are pouring in... here's a quick look at my submission, CartRunner, and what went into making it. Source is included, but be warned, it isn't pretty!
Enjoy.
| |
| Wednesday, December 7, 2005 |
 How to kill yourself in five easy steps |
Posted - 12/7/2005 4:11:31 AM | 1. Obtain a large bottle of A1 Tabasco steak sauce.
2. Obtain a bottle of Tabasco Habanero sauce.
3. Obtain a bottle of Jameson's Irish whisky.
4. Combine. (I'd list proportions, but it was done quite blindly, so who knows. Mix to taste I guess.)
5. Consume. Several slices of fried Spam makes a good base.
My digestive tract hates me now... but damn, it was worth it. That was some awesome sauce.
| |
 Coding and Abstraction |
Posted - 12/5/2005 10:39:24 AM | I'm a sucker for advice. Which isn't to say that I'm particularly keen on receiving it, even on the rare occasions when I have the sense to solicit it. I'm much more keen on giving it, and usually on occasions when it isn't solicited. I'm sure this says something highly unflattering about my character, but I'm not asking for your advice on my character. You're here to get some advice from me. You just don't know it yet.
So bear with me for a few minutes while I invent some advice to give you. In the mean time, I'm going to stall.
One of the things that I like to tell people is to code at the highest possible level of abstraction. I tell them this even if they ask me what toppings they should get on their pizza. I also stole it from people who are much smarter than me (but then, isn't advice all about stealing things from smarter and wiser people?).
The basic idea behind this is that good programming and good design centers around abstraction. Bits are an abstraction of electrical voltage levels. Bytes are an abstraction of bits. Integers are an abstraction of bytes. Beating the digital crap out of zombies is an abstraction of (among many other things) integers. I think there's something that's an abstraction of beating the digital crap out of zombies, and thus ad infinitum, but I think that access to those planes is regulated by Zen and/or LSD.
In practical terms, coding at high levels of abstraction means using the right tools for the job. There's a whole design aspect to it that I could spend a lot of time discussing, but if I do that I'll forget what I really was talking about and we'll all leave with our minds full of garbage and feeling vaguely drugged. In terms of pure coding, though, the big players are tools and languages. I'll focus on languages, since the point I originally set out to make with all this drivelling had to do with languages.
Languages are beautiful tools of abstraction. They're a way to make the intent of a block of code clear to the programmer. CPUs only understand opcodes; few programmers, however, understand them (I sure as heck don't!). Assembler is a step up, because we've got nice english-looking letters and such instead of those icky hex numbers. But the intent of assembler code is hardly clear at a glance, unless the code is laced with enough comments to make War and Peace look like a History Channel factoid. The progression continues up through all the usual suspects: C, C++, Smalltalk, Java, BASIC, Python, et. al. So-called "high-level" languages are high not in terms of being deeply acquainted with reefer, but in terms of being highly abstract. Python knows about strings intrinsically. Assembler doesn't.
High abstraction (usually) means easier implementation. There are some exceptions, but they are fairly easy to recognize when they occur, and I'm making this up on the fly so I'll pretend to have lots of examples, but leave the thinking up of said examples as an exercise to the reader. Games have been using scripting for years as a way to exploit abstraction. Writing code in a scripting engine is much more abstract - and therefore more efficient - than doing it in raw C++, or assembler, or hooking up a battery to the pins on the CPU and tapping in the signals by hand.
Scripting for abstraction has three benefits. First, and most importantly, it makes intent clear. PoliceShip.StartKillEnemiesAndLand(); is a (real life from Egosoft) script command. Isn't that a lot more obvious than three pages of threading code, calls into various AI, collision detection, and 3D rendering libraries, and a handful of housekeeping logic? You don't even have to know KC (the scripting language sampled) to know what that does. The second benefit is that it promotes encapsulation. In KC, there's not a magic function StartKillEnemiesAndLand() that ties directly in to the engine; there's actually a complete game logic system built in the language, and the low-level engine calls are quite a bit more basic and atomic than that. But we never have to worry about them, because they're wrapped in nice simple abstract calls. Entire dramatic battles can be laid out and set into blazing, exploding motion with just a few lines of abstract code. The third benefit of abstraction is that logic is localized to a single place. For instance, if there's a bug in the way one ship does StartKillEnemiesAndLand(), we can fix it once, and all ships will benefit from the fix. The logic for that operation is in one spot, not scattered implicitly across thousands of lines of engine code.
Scripting is good, but it's usually restricted to a simple (and false) dichotomy: engine vs. scripts. One of the things that I've thought about after reading The Pragmatic Programmer is that this should be a continuum, not a set of discrete layers. Of course it's eventually going to resolve into discrete layers (i.e. several different languages), because we haven't invented continuum languages yet.
At Egosoft, we have one of these dichotomies. There's an engine structure, with all of the modules and libraries and such built in, and there's the game logic layer implemented in KC. The KC layer has its own modules, libraries, and structure. It knows quite a lot about the engine, but that knowledge is constrained to wrapper functions and layers. In fact, KC even implements another scripting engine, that is highly abstract. The script engine controls things like AI and various goings-on in the universe. However, it's too abstract; it doesn't provide access to things like the menu system, or the 3D engine. It could, but adding that kind of access is neither easy to build or easy to use.
This is going somewhere... I think. Bear with me while I stall a bit more and pretend to have a purpose. (I'm really just drooling on my keyboard and seeing how long you'll watch before you give up and go play Ninja Loves Pirate.)
Engines, and scripting logic, have implicit layers of abstraction of their own; this is where design comes into play. For those of us who embrace the holy truth of OOP, we've got things like class hierarchies that let us abstract and encapsulate. A typical design has a lot of "basic worker" classes that exist simply to do specific things, and "logic" code that makes use of the workers to actually do something useful, like make the heads on zombies explode. In a scripted design, a lot (but not all) of this logic will be in the form of scripts, perhaps with additional layers of abstraction on top of that.
However, most of these layers of abstraction are split between a very small number of languages. The largest I've seen is four, on X³ (X² also used a similar model): assembler, C++, KC, and the scripting engine. There are implicit layers in each language, even though each layer needs only a specific subset of the language's capability. Specifically, layer of abstraction N needs only the ability to talk to layer (N-1), and the ability to expose functionality to layer (N+1) if needed.
I have a vague feeling that this can be exploited. For instance, instead of writing layers of abstraction in the same language, why not make a simple language framework, and build each layer in a separate "dialect?" Stuff like template metaprogramming in C++ comes close to this, but is still constrained to a single dialect. What I'm thinking is more along the lines of having a "language template" where the basic control structures and syntax is specified, but the available entities are generated dynamically from the lower layer. Basically, you could have an engine layer in C++ (or whatever) that does all of your "do stuff" code, and then a scripting framework engine. We'll call the "do stuff engine" layer 0. Layer 1 can use some kind of info about layer 0 (an automatically generated map of the classes, maybe?) to build a scripting dialect that the script engine can interpret. Then, layer 1 can build up some abstractions and "do stuff" layers of its own, and expose a dialect that can be spoken up in layer 2. Repeat this as much as you need.
The cost? It'd take a lot of up-front work to build such a system, and it would have to be done from scratch. The benefits? Many. Firstly, you get all of your layers in the same dialect. One of the things that bugs the crap out of me with Egosoft's method is that each layer is a different language entirely; I don't know the highest level of the scripting system, but I know the lower three. That seems backwards to me. I should be able to work at the highest possible level of abstraction - always.
The second benefit is localization of knowledge. Having discrete layers promotes encapsulation, and demands a good design. To wit, it ensures that each layer does precisely what it should - no more, no less. If it tries to do more, it will fail, because each layer's dialect doesn't have the vocabulary to do it. If it tries to do less, the system won't run - it may not even compile.
The real bottom line, though, is that each level of abstraction is automatically the right one. Each level is built on the knowledge of the level below it, and the dialect of the scripting language at that level does precisely what it needs to do. Each layer is therefore the optimal layer to do the work of that layer. You don't have to worry about whether or not Language Foo is the Right Tool For The Job; you fabricate the right tool.
I have doubts. I'm not sure if this is really practical in a large-scale project. I have a very clear idea of how I'd do it (down to building the script engine itself and the layer-generation mechanisms) but I'm not really sure how I'd use it in a real-world system. I think it might look different in practical use than in theory; there might be some automatic generation that creates "scripting" that actually is compiled C++ for performance reasons, while non-performance-critical stuff can be done in bytecode compiled languages or even interpreted languages. The cool thing is, if the scripting dialect generator is built right, it should be able to make a dialect that can target any of those endpoints. This means that the same scripting language, syntax, and philosophy can be "compiled" to C++, Foobletch, bytecode, or even straight interpreted. It could even change "compile targets" dynamically; does Layer N not run fast enough interpreted? Drop it down a layer and bytecode compile it. One extra step of preprocessing before your build is done, sure, but if you have a good automated build system that just means you can read one more post on GDNet per build than before. Bytecode not doing the job? Compile it straight into your engine by generating C++ code from the script on the fly.
I think I'll give it a shot with the Habanero engine. I've already sneakily built the basic layers so that they can be transported to other projects trivially. If this multiple-layer scheme pays off, it could usher in a whole new level of reusable code in my own work. That would be cool.
Now I know you've sat through this whole thing, eagerly awaiting the bit of advice that I promised you at the beginning. Well, I don't believe you. I think you just skipped to the end to get the juicy advice, and didn't mess with all that scary-looking nonsense up there. Well I'll show you: no advice! Hah!
| |
In locus hic, omnes res dementes sunt.
|
| S | M | T | W | T | F | S | | | | | 1 | 2 | 3 | 4 | | 6 | | 8 | 9 | | 11 | 12 | 13 | | 15 | 16 | 17 | 18 | | | 21 | 22 | 23 | 24 | 25 | 26 | 27 | | 29 | 30 | 31 | | | | | | | |
OPTIONS
Track this Journal
ARCHIVES
July, 2009
June, 2009
May, 2009
April, 2009
March, 2009
February, 2009
January, 2009
October, 2008
September, 2008
August, 2008
July, 2008
June, 2008
May, 2008
April, 2008
March, 2008
February, 2008
January, 2008
December, 2007
November, 2007
October, 2007
September, 2007
August, 2007
July, 2007
June, 2007
May, 2007
April, 2007
March, 2007
February, 2007
January, 2007
December, 2006
November, 2006
October, 2006
September, 2006
August, 2006
July, 2006
June, 2006
May, 2006
April, 2006
March, 2006
February, 2006
January, 2006
December, 2005
November, 2005
October, 2005
|