About Abstract
Introduction
Philosophy
Design
Goals
“There's just got to be a better way.”
Abstract lets you maintain a single source code tree of a typical GUI application and easily build it for different platforms (namely Win32, OS X and Linux). It offers a platform-agnostic API that can be treated as being “the platform”.
One must settle for the lowest common denominator of what all operating systems provide. However, the common denominator has evolved to a good point.
People familiar with various systems (Mac, Windows, Be, NextSTEP, MFC, etc.) may see familiar design patterns. This is entirely normal -- a lot of good ideas already exist and it would be foolish to ignore them or think that all of my ideas were somehow best.
Most frameworks try to offer all the things that operating systems do. This is wrong, because at a higher level of abstraction, the typical developer is interested in fewer features.
Abstract's approach is to focus on the things that a typical application needs most of the time, and then provide ways to do those things with the least amount of effort and without coupling tightly to the OS.
The typical modern OS has somewhere in the neighbourhood of 5,000 API functions. The typical application might use 500 of them. An OS must be rich because it must be all things to all people. An application is itself an abstraction, because it presents only a hundred commands or so to its users, and offers only a subset of the potential problem-solving ability of the entire OS. But it is exactly this narrowing down that makes applications valuable. They don't do everything, but they automate considerably the things that they do handle.
Here's an example. Want a splitter bar between two windowpanes? Well, normally you would explicitly code for that functionality. But if the panes in question are resizable, and you've placed them next to each other in a parent window, the bar can be automatic. There's no need to provide any more information, because the attributes of the windowpanes themselves are indicative enough of how the interface should behave. If you don't want the splitter bar, you don't think “remove the bar”. Instead, you think “make the panes fixed in size so that there is no logical need for resizing them.” At this level, the entire concept of splitter bar has moved from your solution domain down to the system. It's a feature that falls out as a simple logical consequence of the things you do have to mind (like viewpanes), and it's one less thing to worry about. Or to put it another way:
Nobody wants splitter bars.
What
they want is for users to be able to resize windows.
Here's another example. Consider a window showing a subset of some datum. Traditionally, one adds scrollbars to the window and then writes a bunch of code to allow panning and maybe zooming. But the truth is:
We should be able to just indicate
the datum's size.
The system already knows how large the window
is.
And while we're at it, mouse positions should be reported in
datum coordinates.
Now this is what an application framework is for. Not to be too high-level (like providing scenegraphs, of which there are many kinds), but by automating the tasks immediately above the OS, like relating datum space to window space.
One more example. Let's say you need to display a dialog box to get a set of information from the user, like his age, weight, and height. So you design the box and add various widgets to it. But in reality:
I just want to ask the user his
age, weight, and height.
Given the types of each item, their
order, their allowable ranges and descriptions,
the system should
be able to produce the dialog box on its own.
Traditional frameworks tend to closely mimic the OS APIs they replace. Dialog boxes, GUI widgets, windows and the like don't go away; they just take on different names and different APIs. There's no real fundamental change in the abstraction model. By more properly describing intentions, more UI issues can just “fall out” as a logical consequence.
In Abstract, there's much less code regarding details. In Abstract, you don't think of menus, windows, dialog boxes and scrollbars. You think of commands, datum renditions, user questioning and datum space.
You get to focus on:
Content: The data inside the program
Mutation: What transformations occur to the data
Access: How command events affect the data
Relations: How the various data objects are related to each other
Presentation: How the data is displayed to the user
Persistence: Making some data exist across program invocations or exchangeable with other programs
Developing in Abstract can be initially difficult, because it requires continual, conscious effort to keep design and implementation separate. This is hard not because people resist or dislike doing so, but rather because it is difficult to always know what exactly constitutes design vs. implementation -- the line separating them is fuzzy.
Engineers are also notorious for imagining the solution to a problem while imagining the problem. Instead, you need to sit back, relax, and focus on how the solution exists independantly of any particular implementation. Or better still, focus on what you want to accomplish and keep it seperate from how it is to be accomplished.
For example, your program may offer several commands. Think of the commands solely on their own terms, instead of worrying about what names they have, in which menus they belong, what their keyboard shortcuts will be, etc. Those things are all implementation details. Focus instead on what the commands actually do, and what data they need in order to be treated as persistant objects (so that you can implement full Undo/Redo for free).
Abstract is intended to achieve the following:
To automate today's best practices. For example, most users expect multiple Undo/Redo to be a standard feature. Correspondingly, Abstract should offer data types that capture the feature's various rules (e.g., “When I modify some important datum, I expect to be able to navigate an edit list for it.”).
To showcase most or all of the design attributes that an idealized platform-agnostic system should have.
To not compete with application-level code, such as scenegraphs. Some things are better implemented as separate libraries. Trying to automate too many things increases the system size, slows default build times, increases configuration requirements, and gives people the impression that there is a lot to learn. Small is beautiful, less is invariably more, and brevity is the soul of wit.
To not compete with the underlying platform. For example, the UI experience should be rendered and mediated by the OS, because it was designed to do so. We don't want to make the Macintosh resemble Windows or vice versa.
To avoid favoritism. All supported platforms should have equally good implementations. When deploying a feature, it either runs well on all platforms or on none.
To support C++. This is a popular, fast, and comprehensive object-oriented language.
To have application performance match or closely match platform-specific applications.
To be easy to learn and use, and to let developers think in a “pure” application domain.
To make platform-specific code appear as little as possible at the application level.
To automate those things that typical developers truly don't want to concern themselves with.
To be type-oriented instead of “manager”-oriented, to keep the focus on “what” instead of “how”.
To build easily with the popular development systems.
To not require environment variables or other system-altering things.
To link easily if developers just want a binary library and its header files.
To support real applications, not just “toy” examples.
To support 3D visualization, because that is now a popular application task.
To have solid, working implementations on the most popular platforms. The nicest interfaces don't matter if the implementations are partial.
To have the source code be easy to read, understand, and modify.
To support media capabilities (sound, video, animation) later on. Abstract is intended more for business-style applications.
To have Daylon Leveller simultaneously deployable on Windows, OS X, and Linux. This program will act as the absolute minimum benchmark of application support.
My initial need for Abstract was to port my products to Mac and Linux (in that order). However, the likely rollout phases are:
Implement minimal classes and engines on Windows and OS X.
Develop test application.
Build and test the test application on Windows and OS X.
Commence implementing minimal framework on Linux.
Implement further classes and engines on all three platforms.
Migrate TERDISP app to use Abstract.
Add 3D viewing to TERDISP.
Copy modeling features from Leveller to TERDISP.
Replace Leveller's code with TERDISP's once all features transplanted.
Some of these steps will overlap since they are not strictly dependant on each other. Abstract requires Unicode services so there is no guarantee that it will work on Windows 9x. The improved stability of Windows 2000 and Windows XP, however, are making them the platforms of choice for most Windows users. With a little extra work, however, a non-Unicode version of Abstract capable of running on Windows 9x is possible.
The nature of the test application is undecided. Leveller has a set of heightfield rendering features that should be moved to a standalone rendering program, so that is one possibility.
There are obstacles to abstraction. Either an assumption is unwarranted (poor design), or more commonly, an implementation is insufficient and must be worked around (poor implementation). Java is a textbook case of the latter: the promise of “write once, run anywhere” devolved into “write once, debug everywhere”. Since Abstract is not fully implemented, we have the opportunity to get it right in the design phase. Since Daylon has no commercial interest in Abstract, there is no market pressure to rush and compromise.
Drowning in Metadata
A main impediment to abstraction is that datasources need to be described so that runtime engines can take on more of the presentation burden. This means using -- ugh -- metadata, which means that developers can't just “hop to it” when they code; they have to stop and describe what their programs are storing. This is particularly true for more novice developers who haven't worked on larger systems where abstraction protocols are desirable. As a result, Abstract is admittedly not good for small programs.
Two things are clear: metadata is unavoidable; without it, the framework would have to be an AI in order to accomplish its tasks. But metadata must also not be required solely for its own sake; a proper framework should not pester developers to overdescribe elements ad nauseum.
Seeking Balance - A Philosophical Detour
The challenge of abstraction (or intentional programming) is related to the problem of dualism: in a real sense, resolving the infinite with the finite. When one conceives of an abstract process, it becomes a timeless solution, and is thus temporally infinite. In the real world, however, computers are physical devices that must implement their behavior in real, specific ways, with finite limits. Numbers cannot span any arbitrary range and have infinite precision; memory storage is not infinite; execution speed is limited, etc. So whatever we conceive of in the abstract must somehow be mapped to the physical; and it is precisely this mapping that we wish to automate. The problem can be considered essentially a language translation issue, and like language translation, has proven vexingly resistant to straightforward resolution.
Programming in general is a mix of declarative and procedural approaches. In early systems, the computer offered very raw services (like memory, a video framebuffer, etc.) and the program not only had full access to everything but was expected to abstract these services to the user all by itself. Often times there simply wasn't enough memory to host anything like a modern operating system or even something as compact as DOS. So procedural methods dominated -- if you wanted to do something, you wrote code.
At the other extreme is pure declaration. A normal HTML page is a good example: it states what it wants the text to look like, and code in the browser goes and implements the necessary logic to pull it off. We can think of all data files in a similar manner. They sit there expressing intent, and require some program in order to parse, interpret, and transform that intent into meaningful action and visuals. As a result, a large number of programs are viewers, editors, and translators.
Then there's the gray area in between. An HTML page with embedded JavaScript is not purely declarative anymore. A program that has dialog and menu resources is not purely procedural either. The more declarative a file gets, the more program-side logic is needed to handle the possibilities. This is why today's multimedia formats like QuickTime are gargantuan, because they support declaring sound and video in many ways. The only guaranteed way to reduce all the protocols to one method is to go to the procedural extreme and let programs produce and play the content. But then there's no abstraction at all, and media producers must be programmers. In fact, one hardly needs a media player, since in effect, the programs are their own players.
In the real world, things are both declarative and procedural simultaneously. A paper file with words written on it doesn't need any procedural system to be displayed, because it is inherently visible by simply being a real object. But in a computer, there is no inherent system of physics that performs this automatic magic -- everything that happens must be somewhere programmed to happen. In the real world, the laws of physics act like a premade, single, fixed, reliable, universal protocol that operates on everything everywhere in equal measure. In a computer, protocols are arbitrary, numerous, imperfect, and keep changing. But even in the real world, we layer the basic protocol of reality with our own metarealities such as accounting, scheduling, timekeeping, inventory control, payroll, stock markets, currency exchanges, procurement, command chains, money, power, influence, fame, ownership, relationships, etc. We can think of computer protocols as representing the most extreme of these metarealities, or as meta-metarealities.
So we strive for optimum balance. A system where declarative methods provide maximum benefit but yield to procedural approaches when the benefit becomes more trouble than it's worth. Declarative systems are only as useful as the code interpreting and implementing their intent. Using them requires a willingness to sacrifice perfect representation some (or even all) of the time. Results are best when the differences between implementations are differences that no one cares about, like how GUI widgets actually look (as long as they all look tasteful and consistent).
Copyright
2003-2004 Daylon Graphics Ltd.
Abstract is a trademark of Daylon
Graphics Ltd.
Author(s): Ray Gardener
(rayg@daylongraphics.com)
Location:
http://www.daylongraphics.com/other/abstract/intro.htm
Last
updated: