A Matter of Scope

William Taysom on Thu, 04 Aug 2005


As a frequenter of this fine cubicle, you may have noticed (perhaps to your relief) that I have not posted any essays in the last few months. I've been so furiously engaged that no vaporous thought or steamy idea has condensed into a dew drop essay.

Several have come close, closest being one to catalog the kinds of scope which variables have in programming languages. My index includes six: dynamic, static, hyper-static, object, delegate, and binding dependent. I would very much like to write about them; however, pondering specific kinds of scope causes me to reflect on a more general question, the scope of my whole project. What do I want to do? And just as importantly, what do I want to leave undone? My articles to date have been quite mysterious. No more mysterious than my mental mists. So today I'm putting on the high-beams: what is the project in plain and practical terms?

You may recall that my general interest is in user interfaces. Specifically, interfaces into the digital realm of computing. Many inroads to this vast realm lead to many interesting places, so many that it is hard to explore more than a few. Fewer still because the paths are rugged, uneven, and treacherous. If only the way were less precarious. If only there were a grand highway: fast, safe, free from the obstacles of other paths, then we could see many sights and learn many things.

Paths, trails, and roads connect one place to another. They give you access to something you would not otherwise be able to experience. In other words, they are interfaces. Since interfaces are my interest, improving roads is a great place to start.

But there are so many paths. Which one should I choose? Each leads to a particular place. Therefore, all are of limited use. Each is froth with perils -- potholes, roadblocks, and toll trolls. Therefore, all require much effort to use let alone improve and extend. Still, the number and complexity is most staggering. Ten thousand hands built the inroads to digit world, what good can one person do?

Not much if digital roadways are like physical ones. If you need to get a road done, adding a few hands will only speed the process by a small amount. Many hands make light work? Yes, but that all depends on the kind of light you want to work. A wise man once said, "Adding manpower to a late software project makes it later." If so, then would removing manpower make it earlier? In the material realm, progress is made by adding. In the immaterial realm, progress is made by taking away.

Inconsistency, incompatibility, complexity -- these are the potholes, roadblocks, and toll trolls of software systems: interfaces especially and worst of all since interfaces are meant as means to ends, not ends in themselves. Where do these obstacles come from?


  • Inconsistency comes from limited vision.

  • Incompatibility comes from limited flexibility.

  • Complexity comes from limited understanding.

Pothole Inconsistency

Inconsistencies appear when several parts of a system interfere with each other or act in opposition to one another. For a simple example, look no further than your friendly "Okay/Cancel" dialog: the GUI equivalent of a yes/no question. In some desktop systems, "Okay" goes on the left. In others, it goes on the right. Either side will do. It doesn't much matter which, so long as it's consistent. Likewise, it doesn't matter which side of the road you drive on so long as everyone drives on the same side. You're in for trouble if one person decides to drive on the opposite side. Hopefully, a program getting the buttons backwards won't be as hazardous. It is a hassle, and there's no filling this pothole unless you have both the source and the skills to use it.

Other inconsistencies can be more subtle. When I copy text from one place to another, I'm usually interested in just the words: not their font or size. However, my desktop environment likes to copy all of those formatting details. It's a minor inconsistency (between what I want and what I get), but I don't have any way to fix it, only ways to work around it.

GUI inconsistencies, though highly visible and a little annoying, are far from the worst. The worst kind bubble up from the subtle interactions between different software components. I don't trust any multithreaded application to remain in a consistent state. I can hardly see which part does what to whom during an actual run of a program. To foresee everything that could happen: how presumptuous. Fortunately, there isn't much interprocess-interference in the systems I work with. Occasionally, when one process does start to upset the others, I can control-c to a fresh start. I consider myself lucky. Some environments save interprocess inconsistencies to disk. Others keep a registry for the reason.

Whether small are large, of minor inconvenience or pathological, inconsistency emerges when parts of a system interact in unforeseen ways. Sometimes, tunnel vision is to blame: people forget to examine how their one piece fits into the context of the whole. Other times, the interactions are subtle and externally invisible. In this case, looking over things isn't enough: magnifiers and microscopes are needed determine where the inconsistencies lie. It's hard to fix a problem if you can't see where it is.

Detour: Incompatibility at Work

I used to live in Pittsburgh. It's an extremely hilly place. The saying goes, "If you can see where you want to be, then then you can't get there from here." Have you ever had that feeling when working with a computer? Do you know what a file format is? Or, for a slightly different example, consider this.

Javascript has powerful capabilities for accessing and manipulating the html documents that make up web pages. When you go to a web page, your browser reads the html source, creates a document object model for it, and then you can fiddle with the DOM using JavaScript. All very cool, except I can't seem to use JavaScript to recover the html source. It's a one way trip: you can go there, good luck getting back. If you want your software system to be a two way street, it's likely that you'll need to explicitly describe each way. I wonder why that is?

The Pittsburgh saying captures an subtle truth that is easy to oversimplify. Not being able to "get there from here" is annoying. We could just build roads so that there is a straight path from every place to every other place. It may be impractical in the physical world, but in the digital world things could be different. Yes, but is it a good idea? Limited connectivity creates distance, distance creates space, space gives entities the room they need to live and grow. If everything is connected, then all distances are short, there is no wide open space, and we all step on each other's toes. Hence, namespaces, encapsulation, modularity, memory protection, and network security -- all of these methods keep us from stepping on each other's toes.

The most important part of the Pittsburgh saying is the first part. If "you can see where you want to be", you expect to be able to get there and become frustrated when you can't. It might be an unrealistic expectation. (There's ravine in your way). In that case, make do or do without. Otherwise, for those who persist in unrealistic expectations, there's always the option of building a bridge. (Pittsburgh has hundreds.)

In the computational realm, some ravines are impossible to span. Others are just hard and some surpisingly so. Though frustrating, you just have to deal with them: they're an essential part of the landscape. It's the artificial ravines -- the ones of your own making (or someone else's) those that don't have to be there yet are there all the same -- these ravines can be infuriating. Why are they so infuriating? It's not just the pain of going over, or the hassle of going around. It's the fact that, if you (or the unnamed they) had been more careful in the first place, then you wouldn't be facing this obstacle now.

Here you are, can't go back, can only go forward. What are you going to do about it? You can't just wish roadblocks away. Can't blame bad design. Foresight is difficult even for the wise. For the rest of us, plain-sight is hard enough. Blaming others won't get you out of the mess. Blaming yourself won't either. (Though recognizing your own faults might help you prevent others from cracking open in the future.)

You cannot anticipate every eventuality. Instead, you should prepare for the inevitable. Failures will happen, deal with it. Mistakes will be made. If it's someone else's, you need the freedom and flexibility to fix it. (I don't like begging for bug fixes.) If it's your own mistake, I hope your system is flexible enough that filling a hole or making a bridge is minor delay rather than a permanent detour. Either way, the key to compatibility is being flexible and willing to work things out.

Complexity under the Bridge

Once upon a time, there were three billy goats surnamed Gruff. It's always greener on the other side so they decided to cross a bridge to get there. A wicked troll lived under the bridge and wouldn't let them pass. If trolls are in the habit of giving billy goats grief, I can think of a few creatures who do the same for programmers. Frameworks, APIs, programming environments -- each demands something from their users.

TANSTAAFL. In other words, all software costs something. The cost might not be tallied in dollars and cents, but the price will always include some hours, days, or weeks. (They don't usually put the number on the box, RPGs being an exception, I suppose.) Every creature needs to be kept, fed, tended, and cared for. Digital beasts are no different.

The care and feeding of software critters is an essential part of the hacker's lifestyle. Some approaches to rearing livestock (boxen?) will bring you more happiness than others. Software needs to be properly tamed and domesticated. Discipline takes much effort, but it's easier than letting your software grow up wild, unruly, and unfit for practical purposes.

Without proper diet and exercise, software gets fat and bloated. Bloat comes from too many features, badly integrated, difficult to use. Among many other things, bloat includes conversion between data representations for no good reason and layers of abstraction that don't do much to abstract.

Bloat accumulates gradually. Instead of going through the exercise of truly understanding the problem, it is easy (many times required) to rush an implementation. Once an implementation is born, backwards compatibility ensures it a long life no matter how ill conceived it was.

The java.io package has 77 classes. We have InputStreams, OutputStreams, Readers, Writers, InputStreamReaders, Files, FileInputStreams, BufferedWriters, and LineNumberInputStreams -- to name a few. Why so many? What does each one do? I haven't the slightest idea despite the fact that my job requires quite a bit of Java IO. The java.io package is underpowered and doesn't do me much good. I need multiplexed IO, so I use java.nio. (The 'n' stands for "new".) It adds ten kinds of buffers, sixteen kinds of channels, and a bunch of other classes for a total of 69. It lets me get the job done. (Read this to learn how.) Of course, my applications don't directly use any of these classes. Instead, I made a little wrapper with just four methods: serve, connect, put, and get. It's not much, but it keeps things simple.

Often the preexisting solution is overkill: too many options, no way to tell which one does what you want. So, you end up rolling your own. It's a rickety bridge, but I'd rather risk it than be eaten by an API troll. API Trolls are hard to tame. Tamed ones are tremendously useful. Just remember the saying of Cleveland (my janitor), "It's a dumb beast you foolin' with. It don't never sleep." To train complexity trolls, you need to understand the nature of the beast and the nature of those who will be commanding it.

Solution: Less is More

Inconsistency comes from oversight and lack of foresight. Remedy oversight by viewing parts in the context of the whole. Enhance foresight by fashioning tools that let you see deep and distant places. Remedy incompatibility by providing connectivity where you need it, encapsulation where you don't, and flexibility to shift between the two. Remedy complexity though avoidance and discipline. Pay for lunch upfront but don't pay for a lunch that you aren't going to eat. Capability is the goal, simplicity is the sometimes side effect. Increasing features decreases your ability to use them. Accomplish more by doing less. On the first try, if you rush in, you will end up with an ugly hack. If you plan carefully, you will never finish. Just plan to throw the first one away.

And that's where I'm at: working on the first one. The one I plan to throw away. The one won't see the light of day. What one? One what? I want a software system that is simple, consistent, and plays well with others: a nice desktop environment, one that will let me explore with speed and ease. I want a grand highway though the computational realm. Some ways are plesant. I enjoy them. However, beneath the surface, it's the same old thing.

I want more than a shiny exterior, less on the inside, something simpler. Alternatives, though interesting, are still too much: 2500 classes, some core ones with more than a thousand methods. That much code, it gives me a belly ache. I'm interested in a personal computing environment. An environment that one person can manage. It doesn't need all the bells and whistles. I'm not too interested in low-level details, just application and interface programming. I'm content to run the system on any platform so long as platform specific details don't disrupt the environment's internal consistency. (This doesn't mean I'm impartial. I'd like it to run on my shiny OS and its wind-tunnel hardware.)

The system must be lightweight: light enough so that a single person can understand it. Therefore, a single person ought to be able make it work too. Furthermore, a single person had better be able to explain it to other people. I think that single person is going to be me. So, there you have it. My goal is to build an entire programming environment.

I don't intend to do it alone. Already, hundreds of people have helped me in the form of books, papers, articles, and existing systems. The thing I do intend to do is write the whole thing. At least the first version (which I'll throw away) and the second version (which will be too ambitious) and the third version (which I hope will be worth showing people). I'm striving to be well on my way in four years time. I've already spent more than a year on it, eight months formally.

What does it mean to build a whole programming environment? It needs to be graphical. It needs to be self-contained. It needs networking capability (so it can talk to my other systems). If you think this sounds a lot like SmallTalk or Self, then you'd be right. I wouldn't mind if Wilde were compared to those environments. (I think "Wilde" stands for William's Desktop Environment. It might not. Regardless, the think project is Wilde.)

The graphical part of the environment (Wilo) will be tightly integrated with the scripting language (wl). I mean scripting language in the Larry Wall sense of the word, "A script is what you hand to the actors, and a program is what you hand to the audience. A script is something that is easy to tweak, and a program is something that's locked in." For programing, I want a small core language (Wisp) coupled with a number of system libraries. I describe my ideas in too much detail. (Mainly because the details don't yet exist.) Instead, I'll list some properties that the components might have; as well as, properties they probably shouldn't.

Wilo: The Graphics Subsystem


  • No Crashes (Blue Screen of Death)

  • No Hangs or Stalls (Pinwheel of Death)

  • No popups (Don't interrupt me.)

  • No Accidents (Can always undo.)


    • Fewer Confirmation Dialogs (Boy who Cried Wolf)

    • No Save Dialog (Just Keep it!)

    • Never override the redo buffer. (Ack! I hit the spacebar.)

    • Instead, split the history. (Dude, parallel universes.)

    • Parallel History Merge (It's like totally version control.)


  • Radial Menus (Wiki Menu)

  • No Interference (Secure Controlled Concurrency)

  • No Tooltips (Bane of Existence)

  • Fewer Icons (Can't Read Chinese)

  • No mysteries (Um, what's going on?)


    • Integrated Search (I think it's here somewhere.)

    • Contextual Data Organization (What is this "file" of which you speak?)

    • Profiling (Oh, so half my time is spent in toString methods.)

    • Documented Code (If it isn't in our database, then it doesn't exist.)

    • Causal Analysis (Why isn't my program working?)


  • Modal and Non-modal Panels (Pick your Poison)

  • REPL (Compile on your own time, I want answers now.)

  • Polish comes later (She may not look like much, but she's got it where it counts, kid.)

  • as does integration. (Beware the fire-breathing drag-n-drop.)

wl: The Scripting Language

High thoughts must have a high language. -Aristophanes

This is the subsystem I've spent the most time on. To build an entire environment with the capabilities just listed, I need a good way of describing it. I'm a bit of a polyglot when it comes to programming languages, so quite a few have influenced wl. The most influential (in no particular order) are Ruby, Self, Scheme, ECMAScript, Haskell, and Prolog. In fact, if you imagine wl as being located in the middle of those six languages, then you won't be far from the truth.

wl at the nexus of six languages.

wl is an object oriented language. It uses prototype based inheritance that feels like class based inheritance. It uses implicit, static typing that feels like dynamic (duck) typing. It is a purely functional language that feels like an imperative, event-driven language. Why the dual life? Simple. I want wl to feel familiar while, at the same time, hiding powerful capabilities. In other words, wl is is the programming language super-hero: able to partial evaluate, lazy evaluate, multitask, and backtrack in a single concurrency mechanism. wl is a hit with the ladies: hygienic macros and hardly any parenthesis. Functions, graphs, arrays, hashes, objects, and relational database tables are easily defined and manipulated. Feel free to aspect, mixin, and monad. Data flow bindings included. What doesn't wl have?

Bloat, hopefully. I don't have the time to write a lot of boilerplate. This is the big reason for having the unusual language features. In total, wl should contain somewhere between fifty and a hundred public classes. This figure includes the graphics library. The user guide, manual, and reference should sum up to between 600 and 900 pages depending on the format. I think I can keep that much in my head at once. If not, I'll have to cut out some parts (pacing issues).

Wisp: The Implementation Language

Like Scheme and Haskell, wl will have a small core language. Wisp will have very few constructs (a dozen or so), everything else will be provided as a library: graphics, io, even numbers. What will Wisp include? Just enough to be introspective. If you are familiar Lisp, you'll know that its core is just expressive enough to conveniently write a Lisp interpreter. Wisp takes this idea one step further. Wisp will have the capability to refer to its own constructs as though it were its own interpreter. For example, if Wisp uses a stack, then there will be a Wisp construct allowing you to inspect and affect the stack. In addition to being introspective, fundamental concurrency constructs may find their way into Wisp. That's it for Wisp. That's all. No more.

Thanks for Reading

As you can see, I have my work cut out for me. I'll be sure to update you on my progress when I get the chance. In the mean time, happy hacking!

Tags

Syntax for Wrapping

William Taysom on Fri, 10 Dec 2004


This post started out as an email. As such I assume a lot of context. I apologize, in advance, to my readers who don't know what I'm talking about. However, since I likely only have two readers in the first place and additionally the email was addressed to two people, perhaps I'm apologizing to the wrong people (or in this case none). Regardless.

In object oriented programming, wrapping methods can be a good thing. Wrapping allows for composition which, in turn, reduces excessive subclassing. Even the Gang of Four like it, "Favor object composition over class inheritance. ... Our experience is that designers overuse inheritance as a reuse technique, and designs are often made more reusable (and simpler) by depending more on object composition. You'll see object composition applied again and again in the design patterns."

Unfortunately, the syntax for composition is markedly more difficult in many Object Oriented languages -- basically everything from SmallTalk to Java.

Consider the following.

You have the class Book. A given Book instance will have a Cover. Covers can have a Color. A Book, likewise, has a color. The Book's color is just the color of its cover. That's how we say it in English. Here's how we say it in Java.

Wrapping in Java

import java.awt.Color;

class FrontCover extends Cover {
    private String _color;

    public Cover(String color) {
        _color = color;
    }

    public String getColor() {
        return color;
    }
    
    public void setColor(String color) {
        _color = color;
    }
}

class Book {
    private Cover _cover;

    public Book(Cover cover) {
        _cover = cover;
    }
    
    public String getColor() {
        return _cover.getColor();
    }
    
    public void setColor(String color) {
        _cover.setColor(color);
    }
}

This is okay if you want to wrap one method, but if you want wrap many you need a new line for each. Moreover, if the method has many arguments, things get messy.

// in class Book
    public ReturnObj manyArgs(Arg1 arg1, Arg2 arg2, Arg3, arg3) {
        return _cover.manyArgs(arg1, arg2, arg3);
    }

Wrapping is a hassle.
Wrapping being a hassle leads to less wrapping.
Less wrapping leads to less composition.
Less composition leads to more subclassing.
More subclassing leads to suffering.

Subclassing in Java


class Book extends Cover {}

This code is much simpler. It has the same behavior. However, it says that books are covers. In a way this is true, a book is a cover with extra stuff -- pages for example. But this isn't the whole story and it isn't always true. A book can loose its cover! Using the wrapper implementation, it is easy to add this capability.

// In class Book
    public Cover loseCover() {
        Cover cover = _cover;
        _cover = null;
        return cover;
    }

Using the subclass implementation, it is nearly impossible for books to lose their covers. The book is its cover. You're stuck. You can get away with subclassing for a time, but it isn't the right way. Subclassing is quicker, easier, more seductive -- the way to the Dark Side.

Other essays, might stop right there. The philosopher in me won't.

Why are we forced to either use our powers for good or for awesome? There's no cosmic reason why wrapping has to be a hassle. And in a better language it wouldn't be.

Will's way of Wrapping


a Cover(color) is a Thing
a Book(cover) is a Thing where
    color = lambda cover.color
    color= = lambda cover.color=

Could anything simpler? Yes actually. For example, the parenthesis above are optional. So is the mention of "a Thing", and why the defining of a setter and getter when we just want to say, "A book has a color property that's the same as the color property of its cover." So I write it like this:


a Cover color
a Book cover
    has-property cover.color

Is that good or awesome? Foul? Foul wasn't an option. Why are you calling foul? Of course, I can extend the language like that. I just need a way for saying that 'has-property' is the same the two lines of non-sense I used before. This is called "defining syntax". And my language has a syntax defining capability that will knock your socks off.


`has-property $exp` | exp =~ `$exp.$id` =
    `$id = lambda $exp
     $('$id=') = lambda $exp`

Let's do a little comparison. The Java code is 426 characters. The description in English is 172 characters. My code is 145 characters. This figure includes the definition of 'has-property'.

Good or awesome? You decide.

Tags

A Thread Safe Ring Buffer in Java

William Taysom on Sun, 17 Oct 2004


In a break with my habit of writing full fledged articles, I have a question for you. After spending some time thinking about concurrency (it's been a low priority thread), I'm hoping to put together a catalogue of concurrent programming problems along with concrete examples and solutions -- sort of a "design patterns for concurrent programming" tutorial. (Of course, now that I've been able so say exactly what I want, rubbing the magic lamp produces a genie, "Your wish is my command." Oh well, I might as well share two of the patterns I came up with without the aid of magic. Feel free to post your own in the comments section.

1. Critical Regions: I'm not done yet.

The classic example involves a bank account:

class Account {
    private int balance; // in cents.

    public Account(int balance) {
        this.balance = balance;
    }

    public int getBalance() { return balance; }

    public void deposit(int amount) {
        balance += amount;
    }

    public void withdraw(int amount) {
        balance -= amount;
    }
}

If you make a deposit and a withdrawal at the same time, one of the two operations could have no effect. Since this code happens to be in Java, there happens to be an easy fix:

class synchronizedAccount {
    private int balance; // in cents.

    public Account(int balance) {
        this.balance = balance;
    }

    public int getBalance() { return balance; }

    public synchronized void deposit(int amount) {
        balance += amount;
    }

    public synchronized void withdraw(int amount) {
        balance -= amount;
    }
}

Just adding the keyword 'synchronized' looks simple enough, though you're likely to forget it just where you need it. (I know I have.) Furthermore, 'synchronized' will only save you from a few woes.

2. Race Conditions: What happens depends on who gets there first.

Here's is another common case. I'm sending items from from one object to another. The two objects run concurrently. So to reduce wait time, I want to use a simple ring buffer:

class MessageBuffer {
    Message[] messages = new Message[BUF_SIZ];
    int hd = 0, tl = 0;

    synchronized void put(Message mess)
        throws BufferOverflowException
    {
        messages[tl] = mess;
        ++tl;
        if (tl == messages.length) tl = 0;

        if (tl == hd) throw new BufferOverflowException(this);
    }

    synchronized Message get()
        throws BufferUnderflowException
    {
        if (hd == tl) throw new BufferUnderflowException(this);

        Message mess = messages[hd];
        ++hd;
        if (hd == messages.length) hd = 0;
        return mess;
    }
}

As is, if you read messages faster than they're being sent or send messages faster than they're being read, you have to do your own exception handling. This is good in some cases: you may want to do something exceptional when the buffer is empty (or full). For example, you might want to make sure that whomever you've been talking to hasn't suffered an untimely death. On the other hand, you usually don't assume sudden death (at least I don't). Rather, I just suppose the other party is slow. In that case, I might as well wait. The snippet below folds all the waiting behavior into the buffer class so that I don't need to deal with it.

class BlockingMessageBuffer {
    Message[] messages = new Item[BUF_SIZ];
    int hd = 0, tl = 0;

    public synchronized void put(Message mess) {
        messages[tl] = it;
        ++tl;
        if (tl == messages.length) tl = 0;

        if (tl == hd) waitIgnoringInterrupts();
        notifyAll(); // Wakes all the threads that are waiting to get.
    }

    public synchronized Message get() {
        if (hd == tl) waitIgnoringInterrupts();
        notifyAll(); // Wakes all the threads that are waiting to put.

        Message mess = messages[hd];
        ++hd;
        if (hd == messages.length) hd = 0;
        return mess;
    }

    private void waitIgnoringInterrupts() {
        while (true) {
            try {
                wait(); // Puts the current thread to sleep and
                        // frees synchronization claims on this buffer.
                return;
            } catch (InterruptedException e) {}
        }
    }
}

There's two. Another two come to mind:

3. Deadlock: We're all waiting on eachother.

4. Starvation: I never get a turn.

Tags

On Sign and Reference

William Taysom on Sat, 28 Aug 2004


Language allows is a potent medium of expression. The ability to refer is essential to that expressivity. Being able to talk and communicate means being able to talk about something: being able to refer to it. A sound or a letter is a sign for something else, the signified. When you say, or write, "water" you are using a sign that, for English speakers, signifies a kind of wet stuff. Today, we're going to look at three kinds of signs: icons, indices, and symbols.

These three types are distinguished by how they are able to refer to things. An icon refers by virtue of a resemblance between the reference and the sign. For example, a picture of a shoe can serve as an icon for a shoe. If you go into a shoe store in China with a picture cut out of a magazine, you could get that kind of shoe without needing to know a word of Chinese. Really, you wouldn't even need to bring the cutout; you could just pick the pair of shoes you want and bring it to the cashier. This means that a thing can be an icon for itself. This kind of usage is called "quoting" for some reason.

Quotes and icons will get you though the store, but how do you find the store in the first place? Try looking for building with some placard or plywood board out front. Specifically, a placard with a picture of a shoe on it. In this case, the relationship between sign and signified is less direct. Before, you had picture of an item and the item. This time, the relationship is between a picture of a thing and a place where you get things of that kind. There's still a connection, but it is less immediate and subject to interpretation. (For example, the sign might be for a dance studio.) A sign used in this way is called an index. Some indices are specific: a picture of a shoe needs to point to some shoe related thing (even if the relationship is as tenuous as a thing and a place were the people use that thing to practice dancing). Some indices are more general: the words "this" and "that" are indexical but what they refer to is context dependent. Still, there must be something that connects the sign to its reference. We call this the sense of the sign. The sense is a context specific aspect of the index that lets us determine its reference.

For indices, the relationship between sign and signified is fairly direct. When the relationship is oblique or arbitrary, we say that the sign is a symbol. Symbols are characterized by their arbitrariness. So, the word "this" is almost a symbol. There's nothing about the word itself that shows what this means unless you speak English in which case the sense of "this" is well defined though its reference is context dependent. Thus, the real difference between indices and symbols is how arbitrary they are. For indices the sense is pretty much fixed. Icons, similarly, except more so. Something is an icon by virtue of resemblance -- like a picture. (However, a picture wouldn't serve as a very good icon for a blind person, they don't have the context.) Rather than being three fundamentally different types; icon, index, and symbol are just at different regions on a continuum. Symbols are singled out by an emphasis on the arbitraryness: the symbol's sense depends on the context of its use.

Thus, on the icon-index-symbol continuum we have two extremes. On the one side, are quotations: the case where a thing serves as a sign for itself. On the other side, we have symbol systems: the case where the relationship between sign and signified is completely arbitrary and potentially subject to change. In the middle, we have indices: things that point by virtue of a given context.

Since I've been out of town and haven't posted for quite some time, I'll leave this entry at that. Next time, we'll use these ideas about sign and reference to build a simple language for the case structures we talked about last time. The language will start out very simple, but even in its simplest form, we'll discover that it's Turing complete.

Tags

A Harmony of the Spheres

William Taysom on Mon, 05 Jul 2004


From before the days of Pythagoras, philosophers, dreamers, and the like have searched for a harmony ("fitting together") of the cosmos ("the beautiful order of things"). Pythagoreans thought the heavenly bodies were located at intervals that form a perfect pattern of music: the Harmony of the Spheres. Their cosmology had many peculiar features including a heliocentric view of the solar system. Sometimes the heliocentric view has fallen on hard times, but by the time of Kepler, both the heliocentric view and the harmony of the spheres had been revived.

Fast forward to 1873 when James Clerk Maxwell showed the harmony of light, electricity, and magnetism using a system of four partial differential equations. Since then, other physical forces have been unified: a Nobel Prize in 1979, for unifying electromagnetism with the weak nuclear force (think radioactivity). Others have worked to merge the electroweak force with the strong nuclear force (involves funny things called Quarks). And then there is gravity. Gravity is the odd man out, both weaker and more far reaching than the others. String Theorists (a group not unlike the Pythagoreans) have their own musical theory for combining gravity with the other three forces: vibrations of tiny stings in Calabi-Yau resonance chambers give rise to the four forces which, in turn, give rise to ten thousand things.

My aspirations are not as bold as these. I just want a bit of uniformity when it comes to how we structure our computer programs. Today, I'll give you a taste of what I have in mind.

Last time we looked at how the the edit-compile-debug loop, is tightened (harmonized, "fitted together better") when made into a read-eval-print loop. By following this path to its natural conclusion, we unify the process of programming. But how are we supposed to follow the path? Let's start at the beginning. We want to unify the practice of programming, so what is a program? Some instructions for doing something. Doing what? Manipulating data. Programs are made up of instructions. Instructions act on structures. So, the place to start is with structures.

Structure

The simplest structure I can think of is a point:

01point.png

"A point is that which has no part." A point is atomic, elemental, and pretty pointless all by itself: you can't do anything with it. A lone point has no context, no parts, and no properties. What we really need is two points:

02twoPoints.png

Or even more points:

03morePoints.png

Having a bunch of points creates a context. How so? Look at it this way. Some say that two points define a line. Even if you aren't doing geometry, there's something to that. To have two points means there is some difference between them. If two things are totally indiscernible, then they might as well be identical. This is called Leibniz's Law or the Identity of Indiscernibles. In the diagrams above, location is the differentiating feature. In general, "location" (as in position in a Euclidian space) might not be differentiating. In general, "location" might not be well defined. However, any system that differentiates between points does have an abstract notion of space and location. The space can be very abstract. Distance doesn't need to mean anything. Above, beside, between -- none of these are necessary. At the bare minimum, all you have is here and there, same and different. In such a space, indiscernibility and identity are equivalent by tautology: two points are discernible just in case they are not identical. We'll take this maximally abstract space as our starting place. We've put our point in context with other points. But now, we can't tell which point is ours!

How do we pick out our point? It's easy, we just point to it:

04ourPoint.png

A pointer, like this, is called an index (as in the finger). Since we aren't interesting in all the specifics of fingers, we use an abstract index instead:

05index.png

Notice that the index looks just like a point except that it has sprouted an arrow. We can turn points into pointers by just adding an arrow. Let's go ahead and do that:

06pointers.png

Now, we have a bunch of pointers, but what about the points. Once you have pointers, you don't really need points. A point is just a special kind of a pointer. It's a pointer that doesn't point to anything. Another special kind of pointer is one that points to itself. This kind of pointer is called a constant:

07constant.png

With pointers, we can pick out any point we want, but a pointer that can only ever pick out one particular point isn't very versatile. A versatile pointer picks out different points in different contexts:

08contexts.png

Here we have two contexts. The pointer on the left picks out a different point in each. This is simple, straightforward, yet very subtle. Consider this diagram:

09ambiguity.png

Now, the relationship between the first and the second context is less clear. Did I change two of the pointers? Did I flip the first context upside down? Did I rotate it? It's difficult to tell. So, to keep things simple I'm going to label the points in each context:

10labels.png

This way we can keep track of which pointer is which. Be aware, I'm not adding labels to our system, I'm just adding labels to our diagrams (pictures of the system) so that we can tell which points are the same and which are different: something that we already defined.

Contexts (also called cases) are very useful. But cases aren't yet part of our system. Let's introduce them formally:

11cases.png

Now, cases are first class citizens: they can be pointed to. What's the relationship between points and cases? A point has no parts. If you look inside, there's nothing to see:

12emptyCase.png

Points are just empty cases. So cases are really all we need. They are a generic, yet relatively simple structures: a case contains other cases and provides a context for indices:

13structureSummary.png

Cases are the data structure on which our programs will operate. Today we've only gone over what a case is. Next time we'll add ways of referring to the items inside of a case and the things those items index. Then our system will begin to resemble a language.

Tags

Will's Willow Wisp

William Taysom on Mon, 21 Jun 2004


Today we're going to get down to the meat of it. I want to build the better development environment.

Programming is a tricky. Hardware capability has increased, so have the size and complexity of our programs. A quick find -E . -regex '.*(c|cc|java|lisp|cs)' | xargs wc in my current project directories shows I have a just over a million lines of code to wrangle. The late Edsger Dijkstra once said, "If we wish to count lines of code, we should not regard them as lines produced but as lines spent." A lot of spending has been going on. How can we spend less, save more, and put what we do have to better use?

I can't say I have an answer to those questions, but I do know the challenges I face time and time again. I may have mentioned this previously, but I'm in the business of building interfaces. Some are graphical, some aren't. Even with the ones that are, I don't spend much time where the graphics are: I'm busy on the back side of the screen wiring at the source level. When I want to test things, that's the only time I see any dancing polygons.

Loopy Debugging

To do this, test that is, I close the back panel (except for some break points sticking out), start the engine, rev it up, put it into gear, and drive around until I hit one of the break points. Then I get out to figure out just what's happing, push the machine a few steps forward: everything looks okay. So, I get back in the cab, step on the gas. Snap, crackle, segv -- looks like I continued too soon. I restart the machine and run it to this place again. If I'm lucky, I get a convenient stack trace showing exactly where things went to pieces. It won't show me what could have caused them to go wrong: that's for me to infer from forty levels of stack trace and the values of a few hundred variables. I get out, open it up, tinker, add a printf or two, and run the engine again. This is known as the edit-compile-debug loop. In practice, the loop is usually more elaborate: edit-compile-linkerFailure-editMake-compile-linkerFailure-editMakeAgain-compile-debug-realizeTestCodeHasBug-edit-google-compile-curse loop.

It's doesn't always go that way. On rare occasions, I have the privilege of writing in Lisp. Here the edit-compile-debug loop is tightened into the read-eval-print loop. Type a line, it's read, evaluated, and the result is printed. Debug one function at a time. Keep a transcript and call it a unit test. While dot whoring the other day (searching the dot for opinions, insights, and other dribble), I found a this remark, "I've been reading up on Lisp and have suddenly become disappointed in the entire programming world - right here there's a language with a featureset that it has taken other languages decades to catch up to." And I bet he doesn't even know how to go about implementing a read-eval-print loop: (loop (print (eval (read)))). "Lisp, not as clumsy or random as Java. An elegant language for a more civilized age."

Having a REPL doesn't solve all problems. In fact, it only solves one problem: how to test code in function sized bites. For larger components, components with concurrency and complex io, REP doesn't cut it. More flexible tools are required. Don't let the button nose and mouse ears fool you, SmallTalk does some nice things that we're now seeing in other tools. Suppose we're Squeaking along one day when all of a sudden, Popup!, a ominous stack trace appears. Usually, we just dismiss these beasts, wondering why they bother popping up in the first place. Sometimes we're bold enough to click on some sort of inappropriately labeled "details" button. Squeak doesn't have a "details" button; instead, "debug" lets you jump straight into the debugger. From here, you can sniff round until your heart's content. Nothing special about that. You can make any changes you want, then click "Restart" to unwind the stack and begin again. This already goes a little farther than some other fix and continue systems. However, just having fix and continue isn't going to do you any good unless you know what to fix. The quickest way to find out is to run a few tests. And this is exactly what the Squeak debugger lets you do. It gives you a REPL at every level of the stack trace. Just type an expression, hit command+return, and it will be evaluated in context. Pretty slick.

Debugging is all well and good for mucking through muddy details. But how does one get a bird's eye view of what's going on? How do you do it in a general, comprehensive way that lets you highlight important details or even find out what the important details are?

Debugging Debugging

Asking all these questions begs the question, "Why do we debug in the first place?" I debug to make sure my code does what I think it should. I also debug to figure out how other people's code work. But isn't reading the source enough to know what's going on? Apparently not, and why not? For starters, implementation details can distract from what's really going on. One promise of object oriented programming is being able to hide implementations behind interfaces. Keep the immediate code clean and delegate the details to someone else. Taken too far, endless chains of delegation make object oriented programming into a structured method for writing spaghetti code -- all talk and no action.

Another thing that makes source hard to understand is context dependancy. What happens when you call a function can depend on any number of things. Two calls at different times could produce different results. Some make out functional programming as the art of limiting effects. Limited effects makes for very clean, concise code. Eliminating effects makes one wonder how to use functional programming for time sensitive reactive systems.

A further challenge is the shear size of large code repositories. There's no good way of seeing the whole system at once. Structured programming organizes control flow within a procedure. Object oriented programming organizes state and procedures into a convenient container. But higher level organization requires something else. Inversion of Control is a method for managing dependencies between components, and Aspects let you implement features that cross object boundaries. "One (simplified but useful) way of thinking about the techniques [of] Aspect Oriented Programming is that they put the power of a good debugger into your programming language. ... You need only see a couple examples to realize how powerful and useful this can be. You can then treat some of the rest of the ideas of Aspect Oriented Programming as trying to tame these powerful constructs into something that won't frequently shoot you in the foot (much like Structured Programming tamed the wild goto statement)."

The Will is the Power

This little trek into debugging shows some of the things that are vital in building a development environment. To make programs you need to be able to get inside and work on the program. The source provides one crude interface to how the program works. When you edit the source, your tell the program what it should do in advance. Often, you aren't going to tell it the right thing. To discover what's gone wrong, you might use a debugger, a crude tool to observing the state and control flow of the program. Debuggers become less crude when you can use them in conjunction with editors. As you the edit-compile-debug loop is tightened into a read-eval-print loop the distinctions between program, debugger, and editor become hazy. If we tighten further, the distinction disappears. Running, editing, and debugging become a single, integrated activity. It's like opening up a clock, seeing how it works, and adjusting it without needing to stop it. A system where you can side step the normal interface, and look at it from a different perspective provides a meta-interface. For meta to be workable, I have a few ideas about what the interface needs to be like:

  • Disabling Meta. It should be easy to step outside of the normal flow of a program. However, this capability is not liability free. The meta-view gives you access to some things that are better left untouched. The ability to arbitrarily modify your system means the ability to arbitrarily ruin things. Having full meta-access is like running your system as root. It's not secure for trusted entities, let alone potential abuse. Any meta enabled system needs a good security model: one that gives you the capabilities you want without the responsibilities you don't.
  • Back to the Future: Meta-time. Meta includes having power over the control flow of your system. You need to be able to slow processes down, speed them up, even run them backwards. It's a kind of uber-undo/redo that need to come for free as part of the system.
  • Responsive Modelessness. Meta-control means you can pause or escape from any operation. "I want to not have to ever see an hourglass, spinning beachball, or any blue bar of waiting." A computation may take time, but it shouldn't take your time.
  • Transparency. Not all meta-interfaces are created equal. A good meta-interface is like a polished spyglass. Both the big picture and the minute details need to be represented.

  • Those are a few points. I have a whole cart of them. Programming is a rich and multi-varied experience. So much so, that language is a necessary tool for being able to express yourself. Meta-programming, likewise, requires a language up to the task. There's quite a few languages in the world and some are fairly well suited for the task. Making a good clean meta-interface takes a good clean language to write it in. It should be easy to read, easy to write, and easy to run. It needs to be small, friendly, and powerful. If you have the will, it should have the power.

    What's in a Name

    Will's the name of the language with which the meta-interface, Willow, will be built. I call the meta-interface Willow because it will live and grow in a manner not entirely dissimilar to the way a tree does. Will and Willow need to be easily embeddable in any system that I happen to work on. For this task, I'll use Wisp a stripped version of Will on which the rest of the system can be bootstrapped. That's the vision.

    From now on, my entries will focus on special topics relating to whatever facet of the system I've been working on that week.

    Tags

    Enough with Preliminaries

    William Taysom on Tue, 15 Jun 2004


    When Aaron first said he would give me some room to muse in his cubicle, I wanted to plunge right into the nuts and bolts of my research. As I was writing my first entry (an overview of design goals for my Willow Wisp), better judgment suggested I provide some background. So before dumping my readers into the technical end of the pool, I've gone over a bit of my philosophy. In this entry, I sum up the method behind the madness meant to follow, I can only assume those who are still with me won't mind jumping into a pool full of ping-pong balls.

    The ping-pong pool, if you're just tuning in, alludes to the fact that if we were to try simulating water using ping-pong balls, then some properties of the simulation would be different from the real thing. In particular, jumping in won't feel the same. This "blind men and the elephant" effect is especially noticeable with computers. Just try opening a .pdf document in a plain text editor. Having the bytes isn't enough, you need a .pdf viewer to read .pdfs. The viewer provides the right interface to the document. Interfaces are the name of the game, and Will's Willow Wisp is my playing token.

    To Review

    Computers exist. But even before they did, philosophers learned some interesting things about computation and computability. Unfortunately, the computability approach neglects the first person experience of actually using a computer. (Might be because computability came before computers.) It's a shame since most people are more interested in doing things things with computers than the abstracts of computation. Doubly true for programmers. We don't just do things with computers, we make the programs that let you do things with computers.

    Your average laptop computer is more than a million times faster and has more than a billion times more memory than its giant forbearers of just a few decades ago. The laptop is a mighty machine rendering a 3D scene on its color screen with an unseen beam going between it and some massively multiplayer thing: this is routine.

    Even though eens like this wouldn't have been possible for the dinosaurs of old, some things haven't changed much. We live in a world where *NIX not only continues to roam the earth; but after thirty years, its avian descendant is touted as "the operating system of the future". Odd isn't it.

    Going back to the sixties, the foundation of the internet was being laid in California, Nevada, and Utah. Coming up on it's thirtieth anniversary, the text editor Vi is still loved and fought over. Vi will likely make it to thirty before the next major release of some operating system. Then again, some OS might come out just in time for the golden anniversary of Lisp.

    Lispers have often boasted of the superior power and expressivity of their favorite language. The practice continues. They've even written a song about its Divine origin. Some things haven't changed. Some things won't. Some things shouldn't. The rest is fair game.

    The name of the game, remember, is interfaces. I'm not just talking about those pretty end user GUIs that Aunt Tillie might encounter. The interfaces I'm interested in don't need to be graphical and certainly aren't supported by pretty prepackaged toolkits or easy-glue widget sets. Not all interfaces for defining interfaces are created equal, and I haven't yet found one created in an image that I would call good. This gives me something fun to do. It's good to have purpose in life. I'm looking for something powerful, simple, and philosophically grounded that also plays well with others. I've scoured the earth searching for such a beast. I've found many wonderful and exotic creatures; however, none of them is entirely fit for my dark purpose. So I've decided to take matters into my own hands! I will speak more of this at length, but we mustn't forget about Aunt Tillie.

    Aunt Tillie's Email

    Aunt Tillie doesn't do much with her computer. In fact, the only thing she does or wants to do is check her email. Frankly she could care less about email except she gets messages from her nephew Little Johnny. She doesn't care about bytes on a disk. She doesn't know what a byte is. She doesn't care about files or OSes. She doesn't even care about the words themselves. If she had the words, but they were out of order, she wouldn't know what the message was. Aunt Tillie doesn't really care about the document itself. Suppose she received a message from a clever Nigerian Spammer that just happened to look like something Johnny would send. Aunt Tillie might be tricked, but if she found out the truth, she would be very cross. For Aunt Tillie to be happy, the sender can't be a Nigerian Spammer no matter what the message says. Likewise, if Aunt Tillie can't read the document (she might have misplaced her glasses), then the document won't be worth much. What really matters to Aunt Tillie, so far as email goes, is that she have a certain kind of experience. To have a right or good (shall we say felicitous) experience, certain conditions must be met. We've just listed a few specific felicity conditions. In general though, what matters is that Aunt Tillie be able to experience her nephew Little Johnny. The message is just a vehicle as are the email program and Aunt Tillie's glasses. Aunt Tillie wants to experience Little Johnny. She does this by checking her email. So the subject (person experiencing) is Aunt Tillie, the object of her experience is Little Johnny, and her interface to him is email:

    01subInterObj.png

    This simple diagram overlooks some of the interface details we've already discussed. Johnny sends Aunt Tillie messages, and she can only read when wearing her glasses:

    02subMoInterMeObj.png

    There's nothing stopping us from splitting the interface into more pieces or putting the pieces together in a different way:

    03subInterGlassesObj.png

    The Six Triads

    A message isn't an atomic object: it has parts. There are sentences, words, individual characters, and since Aunt Tillie is reading the message on a screen those characters are made out of pixels. Also, associated with every word and sentence is a meaning. So there's more than one way to decomposed a message. There's meronymic decomposition: a message is made up of several paragraphs is made up of several sentences is made up of several words. There's also semiotic decomposition: combinations of pixels represent words, combinations of words represent ideas. Similar to the subject-interface-object triad, we have the meronymic triad: whole-place-part, and the semiotic triad: meaning-interpretation-sign. As we saw with interfaces, multiple triads of the same kind can be stacked:

    04semMer.png

    When were talk about a message, we end up with a semiotic split because messages are used to represent things. Programming is more than just writing. We go farther: we use magic to turn words into active independent entities otherwise known as executables. I call the abstract shape or form of an entity its essence. The ultimate source for the entities' active behavior I call substance. The thing that joins the two is an implementation:

    05essImplSub.png

    I call this, the triad of being. In addition to the triad of experience, the triad of being, the meronymic triad, and the semiotic triad, I recognize two other triads: the dynamic and the generic. The dynamic triad is used to represent change over time, and the generic triad is used to represent ontological relationships: a cat is a animal is a living thing. In total there are six triads:

    06triads.png

    Interfaces to Interfaces

    You can characterize almost any feature of an interaction, such as Aunt Tillie checking her email, using the six triads. The one missing thing is that there is no way to step outside of an interaction: to view it from a third person perspective. For this seventh aspect, we don't need to introduce another triad, we just need the ability to watch the six from outside.

    The only difference between experiencing and watching is who fills what roles. When Aunt Tillie checks her email, she's the subject, the email program is the interface, and the message is the object. When we watch her check her email, we are the subject, the interface is whatever we use to do the watching, and the object is what we're trying to watch:

    07metaCracker.png

    In this diagram, the email program is playing a dual role. It's both the interface connecting Aunt Tillie to her message, and it is the object of our evil Nigerian plot (or alternatively our efforts to protect freedom.) Either way, the interesting thing is that we are able to observe the interface from the outside. This is accomplished by using another interface: a meta-interface. When programming, we use these meta-interfaces:

    08metaHacker.png

    With a setup like this, we could fix bugs in the email client. The debugger lets us see what's going on, the source lets us prepare changes, and the compiler lets is put those changes into practice. Thus, the debugger is a meta-interface for watching the dynamics of the program, the compiler is a meta-interface for the executable, and the source gives us access to the other triads. Of course, viewing the source requires an interface like good old Vi. Put the text editor, the debugger, and compiler into one integrated package and you can sell a development environment to anyone who isn't fond of using the three tools individually as well as all the other tools you'll need: CVS, make, ant, grep, cscope, and whatever else. Each tool provides a little bit of meta-interface. Each tool represents a lot of hard work. Learning each tool takes hard work too. At the end of the day, if you're very diligent you can fix Aunt Tillie's Email client. You might even be able to save her from Nigerians and freedom fighters. The only problem with all these tools is that getting them to play together is tricky. So much so, that using the tool can take more work than just doing things by hand.

    The Better Mousetrap

    What would a better development environment be like? A development environment is a special kind of interface to programs. It's not like the interface that Aunt Tillie uses. It's a meta-interface. It lets you look inside programs and change things. Which things? Pretty much anything: any of the six triads. So a development environment needs to be a meta-interface in all six dimensions. That's the minimum requirement.

    A good development environment provides good meta-interfaces. Simplicity, efficiency, sufficiency -- all the things that make any program, tool, or system good. It ought to be good enough to serve as its own meta-interface. Thus, if we call our development environment MI, the following should be possible:

    09refIndirect.png

    Many programming systems support this. Those that don't are built on others that do. A better development environment takes things a step further. Not only can it do the meta-interface thing, it can do it reflectively:

    10refDirect.png

    The closer the object part is to the interface part, the more integrated and transparent the object and interface levels become. A step beyond meta object reflection is possible:

    11selfAware.png

    However, self awareness requires that reflection be in place, and only a few reflective constructs are available in programming languages. Functions, objects, even continuations have been given first class status. Aspect Oriented Programming adds principled and powerful reflective features. But all of these taken together do not provide reflection across all six triads. I can't find anything that does. Thus, my quest is to build a development environment that implements reflection on all six triads. I'm not sure what the system will be like, but I do have a name for it: Will's Willow Wisp. Next time, I'll explain the name, and list some features it will implement.

    Tags

    Cartesian Computation

    William Taysom on Fri, 04 Jun 2004


    Descartes begins his meditations by trying to determine what, if anything, can be known with certainty. I cannot trust the senses: "you can't believe everything you see." I cannot trust the way things feel. I cannot even be sure I have a body. Perhaps, some powerful and malignant demon has created all sensation as an elaborate deception. If so, Descartes argues, only one thing remains. Even if there is a demon deceiving me, I am. (In particular, I am being deceived.) To think, to believe, to doubt, to experience requires a thinker, a believer, a doubter, an experiencer, in other words, a first person: I think, I believe, I doubt, I experience. The "I" is the subject of experience.

    Theories of computation have historically bracketed the role of subject. A Turing machine computes a particular function just in case there is some way of matching the machine's behavior to the function. Just watching the machine operate won't magically reveal what it does. Sometimes it's impossible to tell. (This follows from the halting problem.) Moreover, just because two things are equal doesn't mean they're the same. Three plus four equals seven, but "3+4" is not the same as "7". ("7" is one symbol, but in "3+4" is three.) For more on this read Frege. Suffice it to say, if "3+4" were really the same as "7", then we wouldn't need to learn arithmetic, calculators wouldn't perform useful functions, and computers probably wouldn't exist.

    Last time, I said computation has three folds. There is essence: the form or structure. There is substance: the implementation or actual running of it. And there is experience: what you see when it starts, as it's working, and when it's done. The experiencer (the subject) cannot be ignored when considering computation. Likewise, something is being experienced (the object). The object has both an abstract form or essence as well as a concrete form, an instantiation what I have sometimes called an avatar, a body, or golem.

    The subject's experience of an object is mitigated by an interface. Likewise, essence's instantiation in substance is mitigated by an implementation. Just as an interface is a chain of filters connecting subject to object, an implementation is a chain of abstraction layers connecting essence to substance. As you descend the chain, ripping away layers of abstraction, you will eventually reach the primal substance "the dreams that stuff is made of" or maybe it's turtles all the way down. Who knows? You don't usually go too far. Building up from any implementation layer (a layer already connected to substance) is good enough to instantiate essence. For performance reasons, it may help for the implementation to connect at a lower layer, but this approach has it's own drawbacks.

    I think that covers all background and terminology. Since talk is cheep, here's a picture of what I mean:

    The Master Plan

    Notice the diagram has three words in green: Willow, Will, and Wisp. Willow is associated with the interface, Will with the essence, and Wisp with the implementation. Talking philosophy is all well and good, but at the end of the day if you don't have anything to show for it, then what good are you? I intend to be good. The three W's are the three main components of my system. Willow is the user interface. Will is the high level expression language, and Wisp is the low level implementation language.

    Tags

    Beyond Computability

    William Taysom on Fri, 21 May 2004


    What makes a computer a computer? Is it the hardware: CPU, disk, RAM? Is it the software: Filesystem, OS, Applications? Is it the peripherals: screen, keyboard, mouse? This week, we're going to investigate these questions. Our search will take us beyond Turing computably, beyond Searle's Chinese room to the realization that the nature of computation is threefold.

    Previously on C-Muses with William T, we examined the Church-Turing thesis. It says, "Anything that can be computed in principle can be computed by a Turing machine." Furthermore, there is a Universal Turing machine, a Turing machine which simulates all others. Thus, the Universal Turing machine computes everything that can be computed, perhaps even, the entire history of the universe. We got a sense of what this sort of simulation would be like by taking a turn in the Chinese Room.

    The Chinese Room is an isolated chamber, isolated except for a small slot through which you can pass messages. The only remarkable artifact in the room is a book full of detailed instructions. By following those instructions, you produce documents containing interesting and insightful commentaries on every subject. (For kicks and giggles, suppose that book contains all the instructions of the Universal Turing machine as well.) The only problem, for you at least, is that all the insights and comments are in Chinese. If you don't know Chinese, then tough luck, no enlightenment for you. So even if you follow all the rules, you won't be aware of what the messages say. You have to have the right kind of relationship to the messages to know what they say. (In particular, you need to know Chinese.) Searle articulates the idea of right relationship with the saying, "no one would expect to get wet jumping into a swimming pool full of ping-pong balls simulating water." You can't get wet because no matter how well the ping-pong pool simulates water, you won't have the right relationship to the simulation to get wet.

    Before leaving the Chinese room, note one final thing. Though the book in the room has all the instructions for composing sagely messages, it is useless to a Chinese speaker. Since the instructions are in English, there is no way to follow them except through the efforts of an English speaker. However, since the following the instructions produces messages in Chinese, there is no way to understand the messages except through the attention of a Chinese speaker. These three (book, English speaker, and Chinese speaker) each exemplify one fold of computation's threefold nature. I call the first fold Essence. I call the second fold Substance. And the third fold, I call Experience.

    Essence: The Nature of Abstraction

    Usually, if you see a movie in the theater, on VHS, and on DVD, the movie will be the same. (There are a few exceptions.) The screen may be a different size, the quality of the image may differ, but you are essentially watching the same movie. Sameness, for movies at least, doesn't depend on the film, tape, or disk that they're recorded on. Movies are characterized by particular sequences of light and sound. This is the essence of what makes a movie a movie and what differentiates one movie from another.

    Likewise, the essence of a story is its characters and plot. You can tell the same story in different ways. You can write the story in a book, make a movie, or recite it from memory. You can tell long and short versions, but what makes a story a story is removed from the details of a particular telling.

    Likewise, the essence of a triangle is its sides and angles. You can draw a picture of a triangle, but what makes a triangle a triangle isn't to be found in any particular drawing. No line you can draw will be perfectly straight, nor will it be "length without width". Every drawn line will have some color or other, but Euclid never said anything about color. Any drawing of a triangle will be more than just a triangle. The drawing will have unnecessary details. Triangles are abstract entities. They have a form, shape, and structure; but they don't need to have a color. Similarly, numbers are abstract. You can hold three things in you hand. But you can't hold the number three. (Symbols representing the number three don't count.)

    Computer programs are very much like stories, triangles, and numbers. They have form, shape, and structure; but the structure doesn't correspond uniquely to any physical object. You can store a computer program in many different mediums: on your hard drive, on the Internet, on a napkin. It doesn't matter. The form of the program is removed from its physical embodiment. I call this abstract structure or form the "Essence of Computation."

    Physical embodiment does matter when it comes time to run the program. If the program is on your hard drive, your computer needs to have the drive mounted. If the program is on the internet, you need a connection. If the program is on a napkin, you probably need to type it in and run a compiler. To run a computation takes more than its Essence.

    Substance: The Nature of Instantiation

    When it comes to cooking, just having a recipe won't get you any food. You need someone or something (like a breadmaker) to actually go through the steps one by one. The same holds for computer programs. The formal description of a program isn't enough to actually run the program. You need something else to run the program. The program runner can be a laptop, a person, a tinker toy assemblage, or even DNA. All the runner needs to be able to do is follow steps. Whether the following process is electrical, intentional, mechanical, or chemical doesn't matter. Performing a computation requires rules to follow and a performer who follows the rules. I call this rule following process the "Substance of Computation".

    The word "substance" has a long and sordid history in Philosophy. In Aristotle's Metaphysics, the substance of something is it's underlying reality including both formal and material aspects. Other philosophers define things a little bit differently as will I. I call the formal aspect of a computation (the instructions for performing it), Essence. I use "substance" to refer to the actual performing of a computation. Substance, in my sense, isn't so much the material aspect of a computation as it is the motion or process through which a computation is accomplished. Thus, if you care to draw the distinction, the substance of a computation would be identified with Aristotle's third cause ("efficient cause" or the source of motion and change in a thing) rather than its first cause ("material cause" or what something is made of). Aristotle talks about two other causes. His second cause is called "formal cause." It refers to the structure or pattern of a thing, what I call Essence. His fourth cause is "final cause." It refers to the reason or purpose of a thing, and I roughly associated it with the third nature of computation.

    Experience: The Nature of Participation

    Imagine you have a computer that's been sealed behind a wall. Except in this case, the computer has no connection to the outside world. Such a machine could run for years doing calculations, but unless someone eventually finds the computer, the results won't do anyone any good. Or to put it another way, "If a tree falls in the forest and there is nobody around, does it make a sound?" Even if someone is around to see the results, this doesn't mean they'll be in a position to interpret those results. The English speaker in the Chinese room cannot interpret the messages if he doesn't know Chinese. Who you are, your relationship to a computation, how you experience it -- these can make all the difference. Thus, the "Experience of Computation" is the third fold.

    Summing Up

    Quite a bit can and will be said of the three folds. For today, I just want you to remember them: Essence, Substance, and Experience. Essence is the abstract structure or form of a thing. It's the idea for a story minus all the details. It's content minus the container. As you add details, the Essence can become a very detailed specification. Perhaps the specification of a submarine, a better toaster, or a computer program. Until the specification is implemented in the world, until Essence puts on Substance, you'll be able to think about the thing, but you won't be able to use it.

    Substance is the incarnation of Essence in the world. In the case of submarines and toasters, the incarnation is going to be material. Computations, on the other hand, are characterized by motion and change. Hence, the Substance of a computation is efficient (as in efficient cause).

    In addition to Essence and Substance, there is Experience. Experience fills the gap between a person (subject) and a thing (object). Experience characterizes our perception of an object or our participation in an event. If you do not experience a thing, then you do not have any direct knowledge of it. You could, however, learn about it through the experience of someone else. Such Experience passes through a chain of intermediaries. The collective effect of all the intermediary filters in a chain is an interface. Any subject/object pair will have many interfaces, one for each chain of filters between them. Sometimes, the interface obscures properties of the object: we see through a glass darkly. Sometimes the interface is as clean as a freshly windexed window. Sometimes the interface works as a magnifying lens, a microscope, or a telescope. With each interface, Experience differs, perception differs, and understanding differs.

    Tags

    The Room of Doom

    William Taysom on Sat, 15 May 2004


    "You wake to find yourself in a small room. There are no windows. The only exit you see is a hefty mental door with a small panel. The door has no handle. You appear to be in a prison cell. The only noteworthy items are a large leather-bound book, a stack of blank paper, and a box of pencils. You're locked in the Chinese Room. Will you ever escape?" This isn't the start of a text adventure, it's the setting for this week's post.

    Last time we talked about Turing machines (things like computers), and Turing completeness (the ability to do anything a Turing machine can). It turns out that there is a universal Turing machine (one which can simulate all others). Since every model of computation discovered so far is simulated by the universal Turing machine, some people think that the universal Turing machine can simulate anything, even the entire universe. Today we're going talk about what this might mean.

    "You wake to find yourself in a small room. The only noteworthy items are a large leather-bound book, a stack of blank paper, and a box of pencils."

    > Open book.

    "You open the book. The book contains a list of instructions. Page one reads, 'Start here. Follow all the instructions. Don't make any mistakes.' It then tells you, in detailed steps, to draw a certain symbol on a sheet of paper and to slip that paper under the door. Then it tells to continue at the top of page 6. Page 6 says to draw four symbols, save them for later, then go to page 720. Page 720 says to take the symbols you just wrote down, copy them onto a new sheet of paper, slide the new sheet under the door, and continue reading on the next page."

    > Flip through book.

    "You discover that all the pages of the book give similar instructions. The instructions are clear and easy to follow."

    > Turn to page one.

    "You turn to page one."

    > Read page.

    "You begin reading the page..."

    Meanwhile, outside the room, the prison guard notices a piece of paper emerge from under the door. The paper says, "Help!" The guard is surprised to say the least. Soon another paper emerges from under the door. It says, "I am very hungry." The guard runs to tell his superiors, "We have mistakenly imprisoned a Chinese speaker." (He's yelling in Chinese of course.)

    After a few minutes, the guard returns with two others. They are wearing lab coats. One says, "It's no use. The door is already sealed."

    The other says, "Why didn't he say anything when we integrated him?"

    "I don't understand it. If he had just spoken up, then we wouldn't have imprisoned him forever for the the Crime of Ignorance." (He means the crime of not knowing Chinese. It carries an awful penalty in their country.)

    "Look, another message," the guard says.

    Over the next few months, wonderful thought provoking essays and poems emerge from under the door. Though the poet is sealed away in an Eternal Prison for the Crime of Ignorance, supplies can pass through the panel in the door. The closing mechanism never lets anyone see into the Eternal Prison, but they can send food, water, and paper through the panel. Soon they begin to write questions on the paper. The poet gives wise and insightful replies. Truly, this Sage knows the deep things of the world. But, who is the Sage? How does the Sage understand such things? Or does he, understand that is?

    While all of this is going on, the poor soul locked in the room only knows that by following the instructions in the book, he keeps getting fed. He doesn't have the slightest idea what's going on outside. He knows nothing of the Sage. He doesn't even know that the symbols are in Chinese. It just looks like a big, complicated mess.

    When John Searle came up with this story, he said it shows that computers can't really have minds. Computers just follow simple instructions, like the person in the box. Clearly, the person doesn't really understand what's going on: neither can a computer.

    Searle published that argument a half century ago. The Chinese Room has been controversial ever since. Some have been bothered by it, others find it obvious and compelling, some are frustrated, others confused, some have written entire dissertations saying that the argument is deeply flawed in every conceivable way. I remember when I first heard Searle explain the Chinese room. Being much younger and less experienced, I could tell that something fishy about it, but I couldn't say what.

    There are two curious features of the Chinese Room. The first is the room itself, the fact that someone is locked away in a room and no one can see inside. The second is the book. How could a book have instructions that, when followed, fool the people outside the room?

    The first feature is not unique to Searle's Chinese Room. Turing proposed a test for showing machine intelligence. It was a imitation test. Suppose you're IMing someone who you don't know personally. You chat with them for five minutes. If you still believe that the "person" on the other end is really a person (and not just a computer program), then the program passes the Turing test.

    Of course, Turing wasn't the first person to think of something like this. Descartes proposed a similar idea (and dismissed it) in 1637:

    "If there were machines which bore a resemblance to our bodies and imitated our actions as closely as possible for all practical purposes, we should still have two very certain means of recognizing that they were not real men. The first is that they could never use words, or put together signs, as we do in order to declare our thoughts to others. For we can certainly conceive of a machine so constructed that it utters words, and even utters words that correspond to bodily actions causing a change in its organs. ... But it is not conceivable that such a machine should produce different arrangements of words so as to give an appropriately meaningful answer to whatever is said in its presence, as the dullest of men can do. Secondly, even though some machines might do some things as well as we do them, or perhaps even better, they would inevitably fail in others, which would reveal that they are acting not from understanding, but only from the disposition of their organs. For whereas reason is a universal instrument, which can be used in all kinds of situations, these organs need some particular action; hence it is for all practical purposes impossible for a machine to have enough different organs to make it act in all the contingencies of life in the way in which our reason makes us act."

    Descartes has been right for three hundred sixty-seven years and counting. To date, no system has been able to fool someone for more than four or five sentences, let alone four or five minutes. Even if a computer that behaves like a person could be made, the Chinese room experiment shows that it's still hard tell who, what, or even if it understands. The person in the room certainly doesn't understand. Perhaps, the book does, yes the large leather-bound book. Any book that contains not just the teachings of a sage, but any answers a sage would give to any question -- could such a book actually be able to understand Chinese?

    Depends on what you mean by "understand." For now, let's stick with a behavior based definition -- if it looks like a rose and smells like a rose, it's a rose. In this case, if it writes like a sage, it's a sage. Except the book doesn't do the writing. The Prisoner does. Just having the instructions isn't enough. Unless there is someone following them, there are no messages under the door and no sagely advice.

    We might conclude then that only the entire system together (prisoner, book, paper) has sagely understanding. Searle anticipated this answer and disagrees with it. Imagine that over time the Prisoner memorizes the whole book. He still doesn't realize that he's been sending notes in Chinese, but now the system is just the Prisoner. The Prisoner writes like a sage without understanding what he's writing. He seems to have developed the skill of automatic writing. (That might be a little far fetched.) On the other hand, our Prisoner displays the symptoms of someone with dissociative identity disorder. He has, "two or more distinct identities or personality states": one is the Chinese Sage, the other is the Symbol Manipulating Prisoner (or SMP). The paradox of the Chinese Room (that the person appears to understand when in fact he does not) is not rooted in the symbolic nature of computers. The same paradox comes up in the case of dissociative identities. The symbols are a red herring (hence the fishy smell). The real issue is dissociation.

    Identity, consciousness, the sense of self: tricky subjects. I'm content to say that I have a sense of myself, that you have a sense of self, and that most other people do as well. Not only is the self something that senses, feels, and is otherwise acted upon. But, it can act and usually acts freely. In other words, actions trace their origins to the self, and are not forced on the self by something else. Sometimes I may feel compelled by external factors; but, in spite of this, I maintain moral agency: I am responsible for the things I do.

    The prisoner has two senses of self. One, he is aware of. The other is a dissociated identity (which he picked up from reading the wrong kinds of books). The symbols he writes mean one thing to the prisoner (I write these things to get food), and something else to those outside (the great teachings of the Sage).

    The Chinese room shows that relationships are important for meaning. Since the Prisoner doesn't have the right kind of relationship to the information in the book, he doesn't understand that he is writing Chinese. Since the people outside the room cannot see into the room, their relationship to the Sage identity leaves them completely unaware of the Prisoner identity.

    The Prisoner "simulates" the Sage in the sense that there exists a certain kind of relationship between the two. In particular, for every action of the Sage, there is a matching action of the Prisoner. (This relationship does not require awareness on the part of the Prisoner.) Simulation for Turing machines is defined similarly. Something is Turing computable if there exists a motion in the machine for every action of the thing. For example, arithmetic problems "find the product of 182 and 684" are Turing computable. That means you can make a machine such that motions in the machine correspond to doing arithmetic. We call such machines calculators. It is very hard to actually build an arithmetic Turing machine. Once such a machine is built, unless you know how the actions of the machine relate to the operations of arithmetic, the machine's behavior will be very mysterious. Just as mysterious as the symbols are to the Prisoner.

    Meaning is found in having the right sort of relationship to something. The arithmetic machine is potentially useful as a calculator, but only if you know how to use it, only if you know how to interpret the symbols it uses. Being formally equivalent isn't enough. Searle comments, "no one would expect to get wet jumping into a swimming pool full of ping-pong balls simulating water." A ping-pong ball simulator can, I suppose, capture some salient features of water. But it is very difficult to form the right relationship with the simulator so that you get wet when you jump into the pool.

    "Difficult? You mean impossible, right?" No, I mean difficult. As an example, look at this. You see a clip of a marble bust rotating, right? Right, except no real photons ever hit this marble: the entire thing is simulated. The light, the motion -- it's just an animation. What's more, if you look at this, I expect you see a jumble of text. It's a few frames from the clip, all that's changed is your relationship to it (in particular I changed the name of the file to end with ".txt"). Meaning is found in having the right sort of relationships. What determines a relationship? You guessed it, an interface.

    Tags

    Philosophical Preliminaries

    William Taysom on Sun, 09 May 2004


    In which William rambles about the philosophy of computers, computability, magic, and the internet.

    What makes a good interface? Why would the subject of computer interfaces be worthy of philosophical investigation? After all, one hardly thinks of the interface to a car -- peddles and steering wheel -- as a philosophical subject. What makes computers any different?

    A car lets you go from one place to another. It can hold passengers, store goods. You have music and air-conditioning to make the trip pleasant, seat belts and airbags to make the trip safe. A car is an isolated chamber (good for private conversations), an object of beauty, discussion, and something difficult to live without.

    Likewise, a computer lets you get from one place to another. Not so much physically (though they might help) as intellectually. It can hold information, store data. You have music, and it is something difficult to live without. -- Computers are a lot like cars. With a car, you cruise the highway. With a computer, you cruise the Information Highway.

    Highways are made of asphalt or concrete. But what is the Information Highway made of? Hmm. I'm not entirely sure. However, I do know what it's good for: information. Let's go ask the Oracle and see what she has to say.

    "What is the Web?", I ask. As is her nature, she gives many replies: some true, some false; some pertinent, some irrelevant. This one looks good:

    "The World Wide Web is a collection of electronic documents that are linked together like a spider web. These documents are stored on computers called servers located around the world. The Web has evolved into a global electronic publishing medium and increasingly, a medium for conducting electronic commerce.

    "The Web consists of: your personal computer, web browser software to access the Web, a connection to an Internet service provider (ISP), servers to host the data, routers and switches to direct the flow of data."

    Not only are computers the vehicles on the Highway, they are Highway, and any destination will just be another computer. Computers are a lot like cars, but they are also like streets, houses, hill, dales -- the web is virtually it's own world. It's not a physical world. Sure computers are made out of sand, plastic, and metal. But those physical parts are no more essential to a computer, than ink, paper, and glue are essential to a book. The essence of a book is in it's contents, and the same is true of computers. Except computers contain more than just words.

    Written language gives words a staying force that they would not otherwise have. The written word lasts long after the author sets down his pen. Words acquire this same staying force when typed into the computer. In spoken language, some words and expressions are special. In certain situations, when you say things like "Thank you", "We swears", "You're fired", or "I choose you" the very act of saying it makes it happen. Such expressions are called performatives. In some contexts, commands "Make it so" can be performatives. If you type, "echo Hello World" in the right context, the computer will echo the words "Hello World" right back at you. If you then type, "i=1; while :; do echo $i; i=`expr $i + 1`; done", the computer will start counting and it won't stop until you tell it to. Computers aren't just data containers, they do things with that data, you can tell them what to do, and they'll keep doing it until you tell them to stop. If writing is speech which endures, computing is action which endures. It's magic! No really, it is magic. Speaking the right words at the right time causes things to happen in the world. If you are wise and know the right words, you can make your own world. You can share it with others so that they can add their own pages. It's already been done. It's going on right now. There is a wide world web of pages. The Oracle is aware of four billion two hundred eighty five million one hundred ninety nine thousand seven hundred seventy four of them. And yet there are many more of which she is not aware. Magic yes, but how powerful is it? What are the limits?

    Philosophers began investigating the problem of computability long before the first digital computers were made. In the seventeenth century, Gottfried Leibniz built a mechanical calculating machine, a primitive computer. This success emboldened him, and he set to building a machine that could determine the truth value of any given mathematical statement. He never built such a machine. And it is likely that no one will.

    In 1930, Kurt Godel showed two very interesting things. First, in any consistent (without contradiction) formalization of mathematics that is sufficiently strong to define the concept of natural numbers (the numbers 0, 1, 2, etc), there is a statement that can neither be proved nor disproved from within the system. In other words, sorry Leibniz. There's no way to formalize mathematics so that you can determine the truth of every mathematical statement. Godel's second theorem lessens the blow a little. It says that any consistent system cannot be used to prove its own consistency. Since consistency is needed first and foremost in mathematics, no one will ever be able to prove that a formalism of mathematics is consistent. But there is still hope. Every inconsistent system reveals itself by claiming that it is consistent. Thus, any consistent formalism, must be accepted on faith. Only inconsistent systems claim they can prove their own consistency.

    In 1936 two people showed that results similar to Godel's apply to computers. Alonzo Church showed you cannot make a computer with the ability of determining whether two arbitrary chosen lambda calculus expressions (read computer programs) are equivalent. Alan Turing showed that there is no Turing machine (read computer program) with the capability of determining whether or not an arbitrary chosen Turing machine will eventually come to a halt.

    These are some of the limits of what computers can do. But from these negative results, some positive results have been born. It turns out that there is a turing machine that can simulate every single lambda calculus expression. Likewise, there is lambda calculus expression which expresses every single turing machine. Thus, a universal turing machine exists, a turing machine that simulates all other turing machines. This discovery inspired the Church-Turing thesis: every computer or notion of computation can be simulated by a Turing machine. Many other notions of computation have been invented since 1936 and every one can be simulated by a Turing machine. Many can, in turn, simulate turing machines. A system with that can simulate every turing machine is said to be turing complete. Most computer programing languages are turing complete. The ones that aren't are usually specially designed not to be, because if the language is not turing complete, then there will be a turing machine that can tell useful things about all the programs in the language.

    The success of the Church-Turing thesis has lead some to believe that the thesis can be applied to the universe as a whole. In other words, the universe itself may be one enormous turing machine. So, how powerful is the magic? Perhaps, it is as powerful as anything in the universe, though some things are still impossible.

    But how do we harness this power? That, friends, is a role of the interface and a subject for another post.

    Tags
    plants