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 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!






















