Monday, December 27, 2004

Special Characters and Gmail

Not only does gmail filter out ".", it also chops off everything from the first "+".

This enables using tmda or simply filtering on what address you choose to give. (Similar to how some people give vendors their names with different middle initials, in order to track how their name got onto different lists.)

Monday, December 20, 2004

JOtherComboBox

Java is a little wack. Its selection box gui widgets have items which are either editable or not. The following is the necessarily verbose way to create an uneditable selection box that allows adding a new item with an explicit action. (Contrast with autocompletion in an editable field, which provides a very low barrier to adding new items.)

import java.awt.Component;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.ComboBoxModel;
import java.util.Vector;

/**
*
* JComboBox for which selecting a particular item allows editing a new item
*/
public class JOtherComboBox extends JComboBox {
int otherIndex;
ActionListener otherItemActionListener = new ActionListener() {
public void actionPerformed(ActionEvent e) {
JOtherComboBox widget = JOtherComboBox.this;
if (widget.getSelectedIndex() == otherIndex) {
widget.setSelectedItem(null);
widget.setEditable(true);

widget.requestFocus();
widget.getEditor().getEditorComponent().addKeyListener(new KeyListener() {
public void keyTyped(KeyEvent e) { work(e); }
public void keyPressed(KeyEvent e) { work(e); }
public void keyReleased(KeyEvent e) { work(e); }
public void work(KeyEvent e) {
((Component)e.getSource()).removeKeyListener(this);
e.consume();
}
});
} else if (widget.getSelectedIndex() >= 0) {
widget.setEditable(false);
}
}
};

public JOtherComboBox(Object[] arg0) { super(arg0); init(); }
public JOtherComboBox(Vector arg0) { super(arg0); init(); }
public JOtherComboBox(ComboBoxModel arg0) { super(arg0); init(); }
public JOtherComboBox() { super(); init(); }

public void init() {
this.addActionListener(otherItemActionListener);
}

public int getOtherIndex() {
return otherIndex;
}

public void setOtherIndex(int arg0) {
otherIndex = arg0;
}

public static void main(String[] args) {
JFrame frame = new JFrame();
JOtherComboBox comboBox = new JOtherComboBox(new String[] {"one","two","three","four"});
comboBox.setOtherIndex(2);
frame.getContentPane().add(comboBox);
frame.pack();
frame.setVisible(true);
}

}

Friday, December 17, 2004

Databases Suck

That is, what we refer to as relational database management systems suck. They're too monothilic. An RDBMS typically has
  • a persistent store
  • a type system
  • indexing features
  • a transaction manager
  • a query language
  • a network protocol
  • usually a full embedded programming language
All this stuff makes them inflexible, and painful to integrate. People like them because they're completely independent of the rest of your code, even to the extent that a human being can often poke at them without application-specific software. They're also standardized-ish.

One Device

Even if single-purpose devices often work better, when it comes to mobile devices, people want one device that does everything. Otherwise, it can get to be a lot to carry around.

So here are the functions that are candidates for integration:
  • telephone
  • camera
  • image browser
  • audio player
  • storage media
  • broadcast radio receiver
  • audio recorder
  • web browser
  • email client
  • organizer
  • video player
  • video camera
Most of these have already been integrated into at least one cell phone product. When will we see it all come together? What a device to have hackable!

These super-devices will change the world. In order to do it, they'll...
  • use good voice recognition as the primary user interface
  • embrace a more end-to-end approach
Cell phone technology is still very far from end-to-end. Carriers will try to prevent the change, because it will completely overturn their businesses.

Thursday, December 16, 2004

Note about Gnome Storage

Not only will Gnome Storage make finding documents easy, remove the need for "Save", and therefore simplify task management, it will also replace a lot of existing interfaces.

Bookmarks, browser history, email indices, song playlists, photo galleries, "personal information management" views: working with these interfaces composes a large part of the time we spend with computers. Once all their info goes in a common storage layer, there'll be no reason to have many different metadata viewer implementations. (Of course, there'll be a mechanism to customize views.)

But that's not all. The desktop'll get many of the advantages of the web. We'll be able to create links to any object, whether it's an email message, a song, or a todo item. Any object may be bookmarked, and we'll get a history of all the objects that we've viewed.

This means big things for the semantic web movement, because it'll create huge amounts of metadata. Not only will we suddenly have easy, standard access to how often we listen to our favorite songs, we'll also know that they're predominantly in the "mp3s from joe" query folder, for example.

Once most applications can store their data in common, we can give them a common implementation of other fun features, versioning and notification, collaboration and localization.

Monday, December 13, 2004

No Password for Local Users

Do you want to relieve local linux users of having to enter passwords? Follow these steps to disable default remote access, which will make it safe for default accounts to have no password.
  1. create a group "remote", and add users to it that may login remotely
  2. add the following lines to your /etc/security/access.conf:
    +:remote:ALL
    -:ALL:localhost
    +:ALL:LOCAL
    -:ALL:ALL
  3. add the following line to your /etc/pam.d/system-auth:
    account     required      /lib/security/$ISA/pam_access.so
  4. add the following line in your /etc/pam.d/su:
    auth       required       /lib/security/$ISA/pam_wheel.so use_uid deny group=remote
If you have a local domain, you might have to tinker more with access.conf, because the "LOCAL" source specification is funny; it matches any host or tty name without a ".".

This was tested with Fedora Core 2.

Of course, you could take the converse approach: give everyone a password, and just don't require passwords for local logins to accounts in the "local" group. This approach is probably more robust against configuration changes (e.g. if a service stops including "system-auth" it will allow logging in without a password). The first approach however is nice for granting minimum necessary privilege. Maybe both is best. Presumably the second approach would be done with a line in local service pam.d config:
auth       sufficient   pam_listfile.so item=user sense=allow file=/etc/localusers onerr=fail
and this works for gdm at least. "mingetty" (for text logins) is a problem because it shares the "login" pam config with "in.telnetd" for plain-text remote logins. Of course, you could hack it with pam_access again (and /etc/security/group.conf) , but the extra complexity probably isn't worth it.

My Daughter

My daughter is the coolest. She just turned three. A couple of days ago she was watching the Elmo episode about music. ("Elmo" is a friendly, red furry creature, on a segment of Sesame Street for toddlers. Each Elmo episode has a theme.) Elmo's pet goldfish, Dorothy, had a toy drum in her jar. My daughter observed, "Dorothy can't play the drum because she don't got hands. She also don't got a stick."

Wednesday, December 08, 2004

Gcc on Solaris

If you're getting
/usr/include/sys/termios.h:376: parse error before `uint32_t'
then you should be invoking gcc as:
gcc -I/usr/include

Tuesday, December 07, 2004

Better Spam Protection

In short, it's a combination "disposable addresses" and "challenge/response whitelists"...
  1. Everyone in your addressbook can mail you without qualification
  2. Everyone can respond to one of your private messages
  3. Everyone can respond to one of your public messages within a fixed interval of time
  4. Everyone else can mail you by confirming their first message by email
The "whitelist" refers to #1, and it may also incorporate the address of anyone new that you email. It's insufficient because many people send mail from a different address than they receive mail. #2 solves this problem by embedding a cookie in your return address (e.g. JoeSmith+keyword+8769d5.4f88c3@someISP.com). (Many ISPs allow you to receive messages to your account name appended with a plus or minus followed by any string.)

You can embed a hash of the address that you're sending to, plus a hash of that hash. The second hash allows you to check that the embedded cookie is valid. The first hash allows you to create a single return address for each address that you send to. If someone who knows that address ever sends you spam, you can blacklist that generated address only.

When you want to receive responses to a publically posted address, it isn't a question of whether you'll want to eventually blacklist the address, but when. In that case #3, you can embed a cookie which instead contains the date until which you'll accept unconfirmed messages.

Even people who can't use the above methods should be able to reach you #4. All of the those people must undergo the slight annoyance of confirming their first message. Their first message will trigger an automatic response from you explaining the situation. They must respond once to that message, and they'll then be added to your whitelist.

The TMDA program does almost all of the work. #2 requires a two line patch. TMDA out-of-the-box is completely unconfigured, but my configuration looks pretty portable. I'll post it after I've been using it for a bit.

Tuesday, November 30, 2004

Future Interfaces

Computer interfaces should be dramatically improved, in the spirit of mpt's crufty rant. He sounds a refrain "We have the technology, so why....?" I don't think we have the technology yet, but we should.

No one should ever have to "save" a file. This requires a few things:
  1. Unlimited undo and redo
  2. Files should only be named with UUIDs
  3. Files should be easily searchable using metadata
Once we've eliminated the save requirement, we can eliminate the unnatural concept of "open" files and windows. The only natural concept is whether a file is visible or not. Everything else should be a transparent optimization.

We can't just use unix inodes instead of UUIDs for two reasons: they don't work across multiple filesystems and they can't handle being removed and restored. I don't think there is a stable implementation of the metadata interface yet, though Gnome Storage looks promising.

Finally, applications will need to support transparent persistence. We can't just use generic checkpointing because it would be so wasteful. (For example, a web browser keeps a lot of stuff in memory, but it really only needs to persist a scrollbar location, a url (and maybe a cached copy of the url, which it'll need apart from the checkpointed image anyway).) Window managers would also have to be better at managing and persisting window arrangements.

Besides window sizes and locations, we'd like to manage tilesets; we'd like them to remember the fact that we're working with two documents side-by-side. Tzorech iyun, sometimes tilesets just indicate the desire to work on two different things at the same time.

Monday, November 29, 2004

Traits for Python

Since python already has multiple inheritance, implementing traits isn't a matter of adding new functionality, but of restricting the functionality that's already there.

The metaclasses below enable preventing classes from having member data and preventing conflicting methods. (Just have your classes inherit from Trait or TraitUser.)

The next step is solving the Artistic Cowboy Problem in a pythonic fashion.

If you missed my earlier traits entry, here's the traits homepage link again.
#!/usr/bin/python

import operator, types, sets

def get_classdict(cls):
"get name:attr dict from a class"
names = dir(cls)
classdict = {}
for name in names:
classdict[name]=getattr(cls, name)
return classdict

def get_methods(classdict):
"get a set of nonsystem methods from a classdict"
methods = sets.Set()
for (k,v) in classdict.items():
if k.startswith('__'): continue
if type(v) is types.MethodType or type(v) is types.FunctionType:
methods.add(k)
return methods

class TraitClass(type):
"""Metaclass for classes that may not have state,
whose conflicting trait methods must be resolved in extenders,
and who may not extend classes which are not traits."""

def __init__(cls, clsname, bases, classdict):
type.__init__(cls, clsname, bases, classdict)
cls.conflict_methods = sets.Set()
all_methods = sets.Set()
for c in cls.get_method_lists(bases, classdict):
methods = get_methods(c)
cls.conflict_methods |= all_methods & methods
all_methods |= methods
#propagate conflicting methods from base classes
for c in bases:
if 'conflict_methods' in dir(c):
cls.conflict_methods |= c.conflict_methods
for c in bases:
#if c == object: continue
if type(c) != TraitClass:
cls.handle_base_class(c)

def get_method_lists(cls, bases, classdict):
"traits check for conflicts from parents and from self"
return map(get_classdict, bases)+[classdict,]

def handle_base_class(cls, c):
"enforce only inheritting traits"
raise "Trait %s cannot inherit from a class that is not a Trait"%cls.__name__,c.__name__

def __new__(cls, clsname, bases, classdict):
#if a nonexplicit class extends nontraits, make it a trait user
if classdict.get('__metaclass__') != TraitClass and 0 < len([c for c in bases if type(c) != TraitClass]):
classdict['__metaclass__']=TraitUserClass
#if class is actually a trait, make readonly
else:
classdict['__slots__']=[]
return type.__new__(cls, clsname, bases, classdict)

class TraitUserClass(TraitClass):
"""Metaclass for mostly normal classes,
that must use single inheritance of nontraits,
and that implement trait conflict handling"""

def __init__(cls, clsname, bases, classdict):
TraitClass.__init__(cls, clsname, bases, classdict)

unresolved = getattr(cls, 'conflict_methods',sets.Set()) - sets.Set(classdict) - sets.Set(dir(getattr(cls,'nontrait_base',None)))
if len(unresolved) > 0:
raise "conflicting methods",unresolved

def get_method_lists(cls, bases, classdict):
"check for conflicts in parent traits"
nontrait_base = getattr(cls, 'nontrait_base', None)
return [ get_classdict(c) for c in bases if c != nontrait_base]

def handle_base_class(cls, c):
"enforce single inheritance of nontraits"
nontrait_base=getattr(cls,'nontrait_base',None)
if nontrait_base:
raise "Trait-using class %s can extend no more than one class that is not a Trait"%cls.__name__, c.__name__
cls.nontrait_base = c

def __new__(cls, clsname, bases, classdict):
#python weirdness; __metaclass__ doesn't override parent's here
if classdict['__metaclass__'] == TraitClass:
raise "Trait %s cannot inherit from a class that is a TraitUser"%clsname
return type.__new__(cls, clsname, bases, classdict)

class Trait: __metaclass__=TraitClass #convenience object
class TraitUser: __metaclass__=TraitUserClass #convenience object

Python Metaclasses are Weird

$ python <<''

> class m1(type): pass
> class m2(m1): pass
> class c2: __metaclass__=m2
> class c1(c2): __metaclass__=m1
> print 'the type of c1 is', type(c1)
>
the type of c1 is <class '__main__.m2'>

Monday, November 22, 2004

Why XML is Case-Sensitive

# re: Tyranny of the geeks 11/20/2004 1:27 AM Tim Bray

XML markup is case-sensitive because the cost of monocasing in Unicode is horrible, horrible, horrible. Go look at the source code in your local java or .Net library.

Also, not only is it expensive, it's just weird. The upper-case of é is different in France and Quebec, and the lower-case of 'I' is different here and in Turkey.

XML was monocase until quite late in its design, when we ran across this ugliness. I had a Java-language processor called Lark - the world's first - and when XML went case-sensitive, I got a factor of three performance improvement, it was all being spent in toLowerCase(). -Tim

Friday, November 19, 2004

The Way to Manage Windows

Writing a window manager is hard (just like making war). I've no time to do it, so I'm going to describe my vision here.
  1. Windows should never overlap. Windows overlap when one partially obscures another.
  2. Windows should only ever be grouped by tabs or tiles.
Tiled windows are completely visible, and occupy all the available space because their edges line up. You can see an example in WindowsXP. If you have a bunch of windows open for an application, right clicking on that application's taskbar icon will give you "Tile Horizontally" and "Tile Vertically" commands.

Tabbed windows only have a single visible window, which also occupies the whole available space. Optionally, the system can display little "tabs" for switching between windows, or it can provide different mechanisms.

By nesting tiles and tabs, any reasonable window behavior can be represented. "Workspaces" and "virtual desktops" are essentially tabs. "Sticky windows" can be implemented with top-level tiles. The beautiful thing about having so few concepts is that features are available in many contexts.

I'm still not sure what window operations should be available. Dragging a window to a different tabset or tileset should move it, but should we have a mechanism for swapping tiles? It should be possible to change a window from tile to tab and back, but what mechanism should there be for zooming a tileset?

Here's a tentative set of operations: each window gets "pull up" and "push down". For tabs, this is pretty self-evident. For tiles, it involves resizing the partner tiles, so that this tile is in the same place despite the push/pull. In addition to push/pull, tabs get an operation "change to tile", and tiles get two operations : "change to selected tab" and "change to unselected tab". These operations are reversible, so tile positions are recorded (and resettable). The dimensions of the window otherwise determine the default horizontal or vertical tile placement. Drag and drop "moves" both tabs and tiles, and they will become tabs and tiles of the drag target, respectively.

There are window managers that implement tiles, but they all have two problems: they don't use this beautiful dual idea, and they are almost completely unusable.

Unfortunately, since many applications make unwarranted assumptions, the window manager would have to support the legacy behavior also.

Editor Feature

When I cut Levi from "Reuven, Shimon, and Levi", and I paste it to before Reuven, I want my editor program to do the right thing. That is, I want my editor to display "Levi, Reuven, and Shimon" instead of "LeviReuven, Shimon, and ".

My java editor should also do the same thing for commas, but it's really inexcusable that java doesn't allow lists to have trailing commas (1,2,3,).

Thursday, November 18, 2004

Traits for Reuse

Traits are an exciting development in object oriented programming.

Traits are collections of methods, like objects without state. These methods are parameterized over other methods, which may be traditional object methods or other traits methods.

A class may inherit multiple traits, but without the problems of multiple inheritance and mixins . This is because 1) their composition isn't ordered and 2) they support composition operators for resolving conflicts.

Traditional classes still have single inheritence, and they contain all the state and glue code. Traditional inheritance is for subtyping, and traits inheritance is for reuse. The developers are quite conservative; they propose traits as an incremental addition to traditional languages. The traits methods may be visualized as code which is simply copied into their composing classes (almost reminiscent of linked editing).

The End of Even Numbered Linux Releases

Until recently, the Linux kernel development followed alternating cycles of adding new features and then stabilizing. The stabilization period was relatively short, and then development would branch off again. People would then be free to make low impact patches to the stable fork, but most developer attention would be focused on the new development fork.

With the most recent release, Linus has abandoned these alternating cycles. This is partly because new revision control tools allow him to better "cherry-pick", so that he can pull from other developers' own development forks. More importantly, though, the Linux distributions really want to make their own stable releases. They have their own release schedules, which they prefer not to tie to the kernel's schedule. The distribution kernels also get much greater user exposure (and testing) than Linus's own kernel, and so are better positioned to address stability issues.

The mainline kernel has become the permanent "unstable" kernel, albeit a little less unstable. Now the distributions contribute their patches back to the mainline and allow them to be beta tested there. If you want a rock stable kernel, you should get a distribution kernel, or even an enterprise distribution kernel.

Years ago, hackers debated whether Linux should remain a hacker system or if it should clean up its act and "go mainstream". Linux is remaining a hacker system, but companies have sprung up to build their own mainstream versions. It's nice that there's a business model in that.

Lee Revell describes the current situation nicely.

It remains to be seen whether there's a future for multiple distributions. It could be that, just like in the commercial software world, there can be only one. That is, that network effects may favor a single distirbution gaining the overwhelmingly majority of users.

Tuesday, November 16, 2004

My Bookmarklets

Boorkmarklets are bookmarks that consist of javascript code. Here are two that I rewrote:
Right-click on one and select "Bookmark This Link" or "Add to Favorites", and then you'll always be able to use the feature by selecting the bookmark that you created.

Friday, November 12, 2004

Making Things Easy to Maintain

A lot of the cost of software is maintenance. Currently, There Is No Magic Bullet for making maintenance cheap. But there are a lot of best practices which I'm going to try to collect.
  • Make it as simple as possible.
  • Use test driven development.
  • Keep as little state as possible.
And some particular points.
  • Centralize environment configuration.
  • Make jobs rerunnable/idempotent whenever possible.
  • Give temporary resources short-lived unique names.

Tuesday, November 09, 2004

Note About Dune

The two most powerful institutions in the Dune series are the Bene Gesserit and the Bene Tleilax.

The BG is entirely composed of women, and the BT of men. Both houses prefer to exercise their influence quietly, the BG by only manipulating and never controlling directly; BT by assuming a guise of inferiority. In the Dune world, artificial intelligences are absent, so improvement of human beings is the main historic goal. The BG pursue this goal through training the mind and body, and by a "human breeding program", while the BT pursue it through direct genetic engineering. Each institution has its own approach to effective immortality; the BG share the memories of all of their female ancestors, and the BT clone themselves and infuse the clones with their original consciousness.

The BG incorporates motifs of the Christian Church (and possibly the Jesuits), and the BT of Islam. Both houses pursue a Messianic dream. The dream of the BG is to create the "Kwisatz Haderach", but the dream of BT has not been revealed. The Kwisatz Haderach, among other abilities, will have access to the memories of both his male and female ancestors. It's clear that these schools must somehow synthesize, and in fact this has been alluded to in the last books of the series.

Though much of the Dune lingo is taken from greek and arabic, "Kwisatz Haderach" almost certainly refers to the hebrew קפיצת הדרך ("skipping of the way") that figures in Chasidic literature. It's a miracle of travelling great distances in a short amount of time. Accordingly, it was the Miles Teg character, and not "Paul Muad'dib" who became the first Kwisatz Haderach.

Incidentally, Leto, Paul, and Leto II evoke Avraham, Yitzchak, and Yaakov.