One of the neat things computers have become able to do as they become more "personal" - eg, we spend more of our lives operating through them, and they become more portable - is personal information management (PIM).
I remember PIM apps in the early 1990s, running under MS-DOS. Quitting whatever you were doing and starting up a separate app to look at your calendar was a bit unwieldy so they didn't do so well, except perhaps Borland Sidekick, which used some clever tricks to pop itself up over running applications.
But nowadays we have systems like Apple's PIM components in Mac OS X; an address book, todo list manager, crypto keyring and calendar provided with the OS, with nice interfaces for using them yourself and an API for all applications to use the same databases. Mac OS software seamlessly uses the inbuilt PIM infrastructure wherever applicable, for the good of all. It's about as good as it gets, so far.
But it's not perfect.
For a start, the task list is a bit primitive. When I had a Mac, I used the excellent Things.app to manage my tasks, as it supports projects and roles and all that stuff, which helps to keep my hectic life compartmentalised. Since it has a much more rich data model than the OS' inbuilt task list, it has to have its own database to keep it in, but it integrates with the inbuilt one as well as it can. My tasks in Things.app get added to the native task list, without their extra information; and new tasks I add to the native task list appear in Things.app, in the 'inbox' area for unclassified tasks, awaiting my attention to move them to a project.
It'd be nice if the underlying PIM database was flexible, allowing arbitrary properties to be added to objects. Then the native task list viewer could share the same actual task list as third party apps, and it would just ignore the extra information about projects.
But even that would suck a bit. Imagine I also had a task manager app that synched to my smartphone, and let me tag each task with the physical locations I can do it from (eg, DIY must be done from home), so an app on the smartphone can show me tasks filtered by location. I'd then need to juggle two task list apps; both would be a superset of the basic task list app (and would therefore duplicate all the basic display-a-task, deal-with-due-dates, etc logic), but each adding their own extra features. Altering all of the properties of a task would involve finding it in two separate apps. Et cetera.
As it happens, I'm also interested in making more use of knowledge representation techniques in software. Knowledge representations such as Resource Description Framework or Horn clauses have the useful property that information from different sources can be merged, as long as the names of things are agreed upon. They work by storing information, not in tables (like the relational model), nor as a graph structure (as the in-memory data model of most programming languages), but as an unstructured list of statements about objects.
The objects can be literal values - strings, numbers, that sort of thing - or symbols of some kind, used to represent objects that don't (or might not) exist directly within the computer, such as people or concepts. RDF uses URIs as its symbols; Horn clauses (being a mathematical notation) are a bit vaguer as to what a symbol is. Either way, they have to be some identifier for the abstract objects.
Each statement links a number of objects, with a relationship (which is itself an object, usually constrained to be a symbol).
So say we have objects called
cheese (the latter representing the general concept of cheese, rather than any particular lump of cheese), and an object called
like, representing the concept of liking something. We might write something like:
(like alaric cheese)
...to mean "
alaric is related to
like". As it turns out, just about anything can be represented as such statements. From the relational model, a table can be converted into a symbol used as a relationship (the table name will usually do), and each row into a relationship of the objects that are the values of the fields of that row (likewise, we could have a relational table called
LIKES that lists the IDs of objects that like other objects). Relational models usually use arbitrary integers as the "symbols", and automated mapping into knowledge representations is hard because it's not always explicit if an integer column is an identifier, or the identifier of what (as it could be a foreign key), or just a price or some other actual integer quantity. But with a bit of human guidance, it can usually be done.
However, note that in a relational model, that
LIKES table would have two foreign keys into some other table that lists all the objects that can take part in liking. It'd be impossible to say that
like itself (it's nice to like things!), since
like is a table rather than a row in whatever table the foreign key pertains to. The relational model has a very static type system, as people who have tried to map class hierarchies into it often find.
Now, the fun thing about knowledge representations is that the objects are implicit. In a relational system there'd be a table of
People or whatever, giving each person an arbitrary integer as a primary key then listing details of that person. You can point at rows in this table and say "there are the people".
But a knowledge model just has lots of facts about each person, sort of spread around. There's no place you can point at and say "there's the person". If you want a list of all the people, then you need to look for all statements saying
(is-a-person X) ("X is a person"), which is as close as we get to assigning types (not that
(is-a-person X) can coexist alongside statements such as
(is-a-customer X); an object can have lots of types, all overlapping). If you want to delete an object, you need to scan the knowledge base for all statements referring to that object, and delete those.
If my personal information was stored in a knowledge base, then Things.app could share the same basic relationship objects as the inbuilt task list.
(title foo "Feed the cat"),
(due-on foo (date 2009 12 16)), but also add its own:
(title bar "World domination"),
(is-part-of foo bar).
Note the use of generic relationships -
title gives any object a title.
is-part-of is a generic containment relationship. This means that even without knowing about projects and task lists, software can tell that things have names, and that there's some kind of containment relationship to explore. The names we give objects -
bar in my example - are arbitrary; so for objects that don't have a natural name, they could just be random strings, like UUIDs; RDF even lets such objects have no name and be referred to implicitly. Objects we want to use widely (such as relationships) will benefit from nice names, however.
It's easy to merge knowledge bases, too. Just pour all the statements into one file. You have to be careful of the same object having a different name on each side, obviously, and how to fix that can only be handled on a case-by-case basis; RDF has the concept of "inverse functional properties" that are meant to uniquely identify an object (such as email addresses for people) that can be used to detect and automatically merge things, but it's not applicable to all situations.
If I merged my task lists from my laptop and my phone for the first time, for example, I'd now have more objects satisfying the query
(is-a-task X); they'd be seamlessly merged. If I had synched my devices so they both had the task called
foo above, and on one I added some extra statements about
foo, they'd be seamlessly merged in. It gets more fun when there's collisions - if I change an existing statement, for example. If my knowledge base is sophisticated and puts unique IDs and timestamps on each statement then it can spot that it's a change and handle that in the merge; if not, then I might end up with both the old and new statements, and the application has to decide how to resolve that.
But all of this doesn't solve the larger problem: if I have several different apps, each of which add more behaviour to tasks, I still need to find the same task in both of them to see all a task holds.
So let's get rid of the apps. Can we make a general "knowledge browser" that's good enough to just edit the knowledge base directly?
This task can be helped a lot by having statements about relationships. I might install a package of pre-written knowledge that states that the
like relationship normally relates a person to another person, or concept. It might also state that
(like X Y) can be written in English as "X likes Y" (and another rule can state that an object X can be written in English as the title of X, if X has one). All of this can just be more knowledge in the knowledge base. A universal knowledge editor could use these statements to build a user interface, in a very similar vein to a browser using CSS to display some arbitrary XML document.
And yet, I'd still be able to jot down my own made-up relationships.
(is-on-my-christmas-list bob). There might be no vocabulary statements defining what this Christmas list thing is about, but a universal editor might well list "is-on-my-christmas-list" when I look up Bob, and would thereafter be able to tab-complete "is-on-my-christmas-list", having seen it already in the knowledge base; it might even notice that existing objects tagged with "is-on-my-christmas-list" are all also tagged "is-a-person" or "is-a-company", and use that to guide editing - but that's a bit more advanced. So I can install pre-written vocabularies that make everything nice, or I can just make my own up as I go along, and maybe write vocabularies for them later.
Knowledge base systems can also be fed rules. Say I have an appointment database, with appointments of the form
(on-date foo (date YYYY MM DD)), etc.
I might write a rule of the form "
(is-appointment X) and
(on-date X (date YYYY MM DD)) and
(title X (printf "%s's birthday" Z)) if
(birthdate Y (date SOME-YYYY MM DD)) and
(title Y Z)". Then anybody (or anything) that has a birthday in the system will cause an appointment to appear called "foo's birthday" on any date that has the same month and day as the birthday.
What if we know the month and day of somebody's birthday, but not their year of birth? We can just write:
(birthdate foo (date _ 04 03)) (where _ is a special variable symbol that means 'unknown'). Since searching a knowledge base is a matter of pattern matching, that will match a query for
(birthdate Y (date SOME-YYYY MM DD)), and not bind a value to
Finally, they can also read from external data sources. I might tell my knowledge base that
(exists X) if X is a pathname that refers to a file or directory that exists, that
(is-part-of X Y) if X and Y are pathnames that refer to files or directory that exist, X is a directory, and Y is directly within that directory, that
(title X Y) holds if X is a pathname and Y is the last part of it (the filename), etc. The rules can work both ways - if the knowledge manager is directly told
(is-a-directory X) and it isn't, then the extension rules can tell it how to create it.
Then, suddenly, my home directory becomes knowledge, too. I can add statements saying that a given directory tree pertains to a given project. Then my knowledge browser UI can tell me about the files I've worked on as part of a project. Perhaps I could teach it how to open up applications to edit different file types. Or teach it how to read the files itself and understand their structure and turn it all into more knowledge.
Now extend that to email messages, browser bookmarks, my phone's call history, my IM history, my Twitter account...
No comments yet.