Tim Jansen's blog


2003/12/27
10 Things I Hate About XML
  1. DTDs and everything in the <!DOCTYPE> tag is horrible. The syntax is cryptic, the allowed types are odd and the degree of complexity is very high (parameter entity references!). RelaxNG and even XML Schema are much better solutions, and the XML specification could be reduced by at least 75%.
  2. Entity references are not needed in a Unicode world (exceptions: the predefined entities and character references).
  3. Processing instructions are an odd and unstructured mechanism for meta-data about the XML and should not be needed anymore, because namespace’d elements and attributes could achieve the same.
  4. CData sections may be somewhat useful when writing code by hand, but that does not compensate for the complexity that they add to document trees - without them there would be only one type of text.
  5. Different char sets. There’s no real need to allow different charsets in XML, it just hurts interoperability. It should be at least restricted to the three UTF encodings, maybe even only one of them. Allowing charsets like ‘latin1′ is useless if processors are not required to support them.
  6. The lack of rules for whitespace handling. Actually there would be a very simple and sane rule for whitespace handling (always return whitespace unless a element contains only elements and does not have xml:space="preserved” set), but the specs require the XML processor to return even the useless whitespace.
  7. The XML specification should set up rules that specify how to handle namespace’d elements and attributes that are not supported by the application. Right now the schema defines how to handle them and the application will not get any support by the XML processor. Ideally the application should tell the XML parser which namespaces it supports, and the XML specification should define what the XML parser does with the rest.
  8. xml:lang is pretty useless without more rules for the XML processor. It would make sense if the XML parser could somehow only deliver text in the desired language to the application, but without any useful function it just bloats the specification.
  9. XML Namespaces are probably the greatest invention in XML history, but they should be in the core specification. Otherwise the APIs are splitted into namespace-aware functions and those that ignore them. The main problem is that the ‘:’ character has no special meaning in the core specification, so you can have well-formed XML with undefined prefixes, several colons in a single name and so on…
  10. XML Schema should be deprecated in favour of RelaxNG. I haven’t seen a single person who would claim that XML Schema is better. People just use it because of the W3C label.



2003/12/23
Property Syntax Revised
Since I wrote the last entry about properties, the comments and Groovy changed my mind about the property syntax:
  • I think the accessor method syntax that panzi proposed is much better than my Java-like syntax or the C# syntax.
  • If the language uses the ‘virtual’ keyword for virtual methods, virtual properties (properties without associated member field) can not use ‘virtual’ as a keyword. Otherwise it would not be possible to override the accessors in a sub-class. But the keyword is not needed anyway, because the new accessor syntax can unambigously define a property. You just need to write one or both accessor methods. For the API documentation only one accessor method must be documented, and it should be documented like a field (and not like a function)
  • Groovy has the simple and effective idea that all public field members are properties. This removes the need for the ‘property’ keyword and also the difference between properties and fields. Just add a regular member, and it is accessed using auto-generated accessor methods, that can be overwritten by you
  • There’s one drawback when properties are accessed like field-members: you can’t control anymore whether you access the field directly, or using the accessor methods. This can only be avoided with a syntax extension, and I think the least painful is the following: a method can access the raw field member of the object class without the accessor methods by prefixng the name with a ‘@’. It is not allowed to use this kind of access for other instances or classes (thus only ‘@field’ is allowed, but never ‘ref.@field’).
    In order to prevent errors, the accessors must not call themselves and thus the attempt to read the field without ‘@’ would cause a compilation error.
Here is the example class with these changes:
class SubString  {
        private int mEndIndex;

        /// Documentation!      
        public int beginIndex;

        /// Documentation!      
        public int length.get() const {
                return mEndIndex - @beginIndex;
        }

        public void length.set(int value) {
                mEndIndex = value + @beginIndex;
        }
};
It’s short. I am not very happy about the ‘@’ thing though.



2003/12/22
Programming-by-contract in C-based languages
Programming-by-contract in C-based languages I do not know much about Eiffel (and I can’t stand its Pascal-like syntax…), but the Eiffel feature that I like is design by contract. Design by contract means that function interfaces, the APIs, are seen as a contract between the creator and the user. In most languages this contract is mostly written down in the documentation. Eiffel has them written in the source code. It is also possible to do this in a language like Java, but Java’s syntax needs a small modification to make this really comfortable. Let’s take this function as an example:
        /**
         * Computes the speed in meter per second from the given distance 
         * and time. The time can be given in seconds, minutes or hours, 
         * depending on the content of timeUnit.
         * @param distance the distance in meters, always >=0
         * @param time the time, either in seconds, minutes or hours, depending
         *              on timeUnit. Must never be <=0.
         * @param timeUnit the unit of the time. Can only be "s" for seconds, 
         *              "min" for minutes or "h" for hours. Other strings are 
         *              not allowed
         * @return the speed in m/s. Guaranteed to be always >= 0.
         */     
        float computeSpeed(float distance, float time, String timeUnit) {
                if (timeUnit.equals("s"))
                        return distance / time;
                else if (timeUnit.equals("min"))
                        return distance * 60 / time;
                else
                        return distance * 3600 / time;
        }
This example will work fine, as long as the user does not violate the API contract. But if the user breaks the contract, the function will either crash (time == 0 causes a division by zero), violate its contract (negative return value if distance or time are negative) or silently return a wrong result (unknown unit in timeUnit). What Eiffel does is having a special syntax for assertions that are executed before and after the actual function. You can find out more about Eiffel’s syntax here. When writing in a language like Java, assertions in the function head are easy to do with either ‘assert’-statements or a couple of ‘if’s. I prefer the latter for argument checks in APIs, since a assert will not produce an error message that is useful for the user: only the function’s developer knows the internals of the function and thus will be able to understand the assert’s error message. For the return value ‘asserts’ are ok, because every error in the return value is a bug in the implementation. The problem with assertions for the return value is that they are quite ugly if your function has more than one return statement and they blow up the code:
        float computeSpeed(float distance, float time, String timeUnit) 
                throws IllegalArgumentException {
                if (distance < 0)
                        throw new IllegalArgumentException("distance must not be < 0");
                if (time <= 0)
                        throw new IllegalArgumentException("time must be > 0");


                if (timeUnit.equals("s")) {
                        float r = distance / time;
                        assert r >= 0;
                        return r;
                }
                else if (timeUnit.equals("min")) {
                        float r = distance * 60 / time;
                        assert r >= 0;
                        return r;
                }
                else if (timeUnit.equals("h")) {
                        float r = distance * 3600 / time;
                        assert r >= 0;
                        return r;
                }
                else
                        throw new IllegalArgumentException("Unknown value in timeUnit");
        }
The code can be simplified using a try/finally, but the syntax is not much shorter (I know that the try/finally does not make much sense in this example, but when the function modifies an object it is often neccessary that you check the object when the code threw an exception):
        float computeSpeed(float distance, float time, String timeUnit) 
                throws IllegalArgumentException {
                if (distance < 0)
                        throw new IllegalArgumentException("distance must not be < 0");
                if (time <= 0)
                        throw new IllegalArgumentException("time must be > 0");
                
                float r = 0;
                try {
                        if (timeUnit.equals("s")) 
                                r = distance / time;
                        else if (timeUnit.equals("min"))
                                r = distance * 60 / time;
                        else if (timeUnit.equals("h"))
                                r = distance * 3600 / time;
                        else
                                throw new IllegalArgumentException("Unknown value in timeUnit");
                } 
                finally {
                        assert r >= 0;
                }
                return r;
        }
A more elegant way to solve the problem is to extend the Java language a little bit and to add a feature that I call ‘method finallys’. They work like the ‘finally’ clause of a ‘try’ statement, but use a function body and allow the inspection of the function’s return value. The syntax would like this:
        float computeSpeed(float distance, float time, String timeUnit) 
                throws IllegalArgumentException {
                if (distance < 0)
                        throw new IllegalArgumentException("distance must not be < 0");
                if (time <= 0)
                        throw new IllegalArgumentException("time must be > 0");
                
                if (timeUnit.equals("s")) 
                        return distance / time;
                else if (timeUnit.equals("min"))
                        return distance * 60 / time;
                else if (timeUnit.equals("h"))
                        return distance * 3600 / time;
                else
                        throw new IllegalArgumentException("Unknown value in timeUnit");
        }
        finally (float r) {
                assert r >= 0;
        }
The ‘method finally’ makes it possible to check the return value and optionally also the object’s state when the function did not throw an exception. To allow the check for void functions I would suggest the syntax
        void methodname() {
        }
        finally (void) {
        }
and for checks that are always executed, even when the function threw an exception, the syntax
        void methodname() {
        }
        finally {
        }
And, of course, it should be possible to use both ‘finally’ variants for a single function.



2003/12/20
Resource management in garbage collecting languages
Resource management in garbage collecting languages One of my favorite C++ features is resource management with stack-allocated objects. It can hardly get more convenient than writing
	{
		QMutexLocker m(myMutex);
	}
to protect a resource from concurrent access. Java even introduced a keyword (synchronized) to get the same effect, but it is only useful for thread synchonization. In C++ you can use the same mechanism for everything, from files to database transactions. Java gives you the choice between creating a try/finally block to deallocate the resource explicitly and keeping the resource until the object is finalized, which may keep the resource allocated for an infinite time. It’s a common error that people write something like this
	void myNaiveMethod() throws Exception {
		FileWriter fw = new FileWriter("file.txt");
		doSomething();
		fw.write("Everything is fine");
		fw.close();
	}
The function looks ok, but if doSomething() or write() throw an Exception the FileWriter will not be closed. This is especially nasty in long running systems, like servlets or JSPs in a web server, because the errors caused by code like this are hard to reproduce. They depend on whether the garbage collector finalized the object before the resource is needed again. The right way to do this in Java is to write
	void myCorrectMethod() throws Exception {
		FileWriter fw = new FileWriter("file.txt");
		try {
			doSomething();
			fw.write("Everything is fine");
		}
		finally {
			fw.close();
		}
	}
Using try/finally is tedious, and the code becomes hard to read when you have two or more resources allocated, as you need to nest the try/finally blocks then. In other words, Java really sucks at resource management. That does not mean that C++’s mechanism is completely safe. It guarantees that the resource will be deallocated, but C++ makes it easy to shoot yourself in the foot:
	void myFunction() {
		QFile f("file.txt");
		writeSomething(&f);
	}
This will only work if writeSomething() does not keep the pointer to f. If writeSomething() does keep it, you will have a nasty bug. Especially when writeSomething() uses it only in rare cases.

C# came up with an interesting solution: the ‘using’ statement. A class that implements the IDisposable interface can be notified when a ‘using’ block is left:
	void myFunction() {
		using (StreamWriter sw = new StreamWriter("file.txt")) {
			writeSomething(sw);
		}
	}
After writeSomething() the system will invoke the dispose() method of StreamWriter that tells it to free the resource. This solution is better than try/finally, but still has problems:
  1. If you need to allocate several resources, you have the same nesting problem as in Java
  2. If writeSomething() keeps the reference, you have lost. It is not at bad as with C++, because you have a better chance of getting a usable error notification, but the problem is not completely solved either
  3. The syntax is not as short as C++’s. Actually it seems to be annoying enough that C# got another statement, ‘lock’, that does exactly the same thing as Java’s ’synchronized’.
Problem 2 is the most difficult one. In an ideal world an error would be thrown if there is still a reference to the StreamWriter after the ‘using’ statement’s block. However that is almost impossible with a garbage collector, unless you would enforce the garbage collection immediately after leaving the ‘using’ block. This may be a nice thing for a debugging mode, but not for production code. Reference counters would alse solve the problem quite easily, but they have other problems. Combining both may be a solution though. (Anyone who knows a system that combines both? I wouldn’t be surprised if that would be faster than a pure GC, because of the better caching behaviour)

The problems 1 and 3 are just syntax problems. Number 1 could be solved by making the ‘using’ statement use the current scope. When the current scope is left, the object will be notified:
	void copyFile() {
		using StreamReader src = new StreamReader("source.txt");
		using StreamWriter dest = new StreamWriter("destination.txt");
		copyStream(src, dest);
	}
Problem 3 is mainly caused by the redundant syntax of creating an object and assigning it to a local variable. Both Java and C# took the C++ syntax for heap-allocated object creation. This makes the code easy to understand for C++ programmers, but it does not make sense when you can only create reference types. So why not eliminate the redundant new and use the C++ auto-allocation syntax for initializing references? This would not only benefit the ‘using’ keyword, it would also shorten a lot of code:
	void copyFile() {
		using StreamReader src("source.txt");
		using StreamWriter dest("destination.txt");
		copyStream(src, dest);
	}
Now it is almost as convenient as C++ code. The last difference is the ‘using’ keyword. One possibility is to replace it with a keyword in the class declaration, so the class is always notified when its creation scope has been left. But this would be a bad idea, because often it may makes sense to make a resource live longer than the function that created it. Another reason is that it would make the code harder to read - unlike C++ there is no difference between auto-allocated and heap-allocated objects. There is one problem with the syntax though: what happens when the developer, for whatever reasons, modifies a variable that has been created with ‘using’?
	void copy2Files() {
		using StreamReader src("source1.txt");
		using StreamWriter dest("destination.txt");
		
		copyStream(src, dest);
		src = StreamReader("source2.txt");
		copyStream(src, dest);
	}
If a local variable is declared with ‘using’ and it is changed, the original object should be disposed. And when the function’s scope has been left, the new object should be disposed as well. As a final optimization, it would be possible to allow ‘using’ also as a modifier for a ‘anonymous’ constructor invokation:
void main() {
	copyStream(using FileStream("source.txt"), using FileStream("destination.txt"));
}
I think a ‘using’ modifier would be a better solution for the resource management than Java’s try/finally and C#’s ‘using’ statement. There’s another challenge left: it is often important to know whether a function will keep a pointer or not, in C++ even more than in Java and C#. Right now the best practise is to point out in the documentation whether a function will keep an object and for how long. What’s a good syntax to state this in the function prototype and ensure that the function will not keep a reference otherwise?



2003/12/18
Property Syntax
Property syntax Today I realized that the property syntax of in all C-based languages sucks. Lets a assume a very simple class called ‘SubString’ that describes a fragment of a string and has two properties: its length and the index of the first character of the string. The class implementation has two field members, one containing the first character’s index, and the other the index of the first character after the string. One problem is that there is no field member corresponding to the second property. This is what I call a virtual property. In this simple example there is no real reason for having a virtual property, but in reality this happens quite frequently when you want to expose a value of an aggregated object. Let’s try to implement this in Qt/C++, Java and C#. Qt/C++ Qt’s situation is a little bit difficult, since there is no language support for properties. moc does not help with the syntax, so the implementation is macro-based. The example looks like this:
class SubString : public QObject {
        Q_OBJECT
        Q_PROPERTY( int beginIndex READ beginIndex WRITE setBeginIndex )
        Q_PROPERTY( int length READ length WRITE setLength )
private:
        int mBeginIndex;
        int mEndIndex;
public:

        /// Documentation!      
        int beginIndex() const {
                return mBeginIndex;
        }
        /// Documentation!      
        void setBeginIndex(int value) {
                mBeginIndex = value;
        }
        /// Documentation!      
        int length() const {
                return mEndIndex - mBeginIndex;
        }

        /// Documentation!      
        void setLength(int value) {
                mEndIndex = value + mBeginIndex;
        }
};
The problems of the Qt approach are:
  • You need to write 2 redundant functions (beginIndex() and setBeginIndex())
  • You need to document 4 functions, instead of only 2 properties
  • The macro Q_PROPERTY is ugly and almost guarantees bad error messages when you get the syntax wrong
Java Java uses a very simple solution for properties that makes use of its reflection mechanism: for a property X you only implement two methods getX() and setX(). The SubString class implemented in Java looks like this:
class SubString {
        private int mBeginIndex;
        private int mEndIndex;
        /// Documentation!      
        public int getBeginIndex() {
                return mBeginIndex;
        }
        /// Documentation!      
        public void setBeginIndex(int value) {
                mBeginIndex = value;
        }
        /// Documentation!      
        public int getLength() {
                return mEndIndex - mBeginIndex;
        }
        /// Documentation!      
        public setLength(int value) {
                mEndIndex = value + mBeginIndex;
        }
};
The advantage of the Java solution is that it only uses Java 1.0 features. No macros were needed or new language features added when JavaBeans have been introduced with Java 1.1. Disadvantages are:
  • You need to write 2 redundant functions (getBeginIndex() and setBeginIndex())
  • You need to document 4 methods instead of 2 properties
  • Some people feel uneasy about encoding the property name into the get/set methods. It also creates some (theoretical) problems with property names that differ only in capitalization
C# C# has built-in support for properties, but with a somewhat strange syntax (note the implicitly defined value variable in the example below). They will not be accessed like a function, but like a field member. By omitting the get or set part it is possible to create read-only and write-only functions. The example looks like this:
class SubString {
        private int mBeginIndex;
        private int mEndIndex;
        /// Documentation!      
        public int beginIndex {
                get {
                        return mBeginIndex;     
                }
        
                set {
                        mBeginIndex = value;
                }
        }

        /// Documentation!      
        public int length {
                get {
                        return mEndIndex - mBeginIndex;
                }

                set {
                        mEndIndex = value + mBeginIndex;
                }
        }
};
Properties in C# have the following disadvantages:
  • You need to write redundant get/set accessors for the ‘beginIndex’ property (you can not convert a regular field into a property without losing binary compatibility, which is the main reason for making them, nor can a field appear as property)
  • Because properties are accessed like fields, many APIs in .Net become a strange mixture of fields and methods, and it is hard to remember which value is exposed as a property and which as a method
  • Many people, including me, find the syntax quite awkward, and it blows up the language’s syntax somewhat. It is quite brief though, compared to the Java and Qt alternatives
The property syntax should be short and crisp Most property accessors are used to access a field member of the class. They are used either to make the properties browsable, e.g. for a GUI editor or to allow automatic mapping to some file format, or to keep binary compatibility even when the implementation changes in the future. Thus syntax of properties should be short to promote their use. When you have 20 potential properties in a class many people, including me, hesitate to use them. The reason is that then would have to write and document 40 methods, declare 20 field variables and 20 properties. Too much work, and that’s why people start working around this by using maps/hash tables for the values (which makes it impossible for the compiler to find many bugs). How easy could it be? A very simple solution for a property would be to add a keyword to promote a field member to a property:
property int beginIndex;
Then the compiler could create default accessor methods that can be overridden by the user, if the property should need specialized accessors. For properties that do not need a corresponding field member, like ‘length’ in the example, the property could be declared as ‘virtual’.
virtual property int length;
The class would look like this:
class SubString  {
        private int mEndIndex;

        /// Documentation!      
        public property int beginIndex;

        /// Documentation!      
        public virtual property int length;

        public int length() const {
                return mEndIndex - beginIndex;
        }

        public void setLength(int value) {
                mEndIndex = value + beginIndex;
        }
};
For most properties this reduces the number of lines down to 1. The disadvantage is that the implicitly defined accessor methods may confuse a reader who isn't aware of them.



2003/12/16
Thought Experiment: XML integrated into a C-like language
Thought Experiment: XML integrated into a C-like language In recent days I made the following thought experiment: how can XML processing be made easier by integrating XML support into a Java/C#-like programming language. I created the code snippet below to try out what such a language could look like. The syntax of this theoretical language:
  • Adds two hybrid-base types called Node and NodeList to the language. Hybrid means that they are Objects like java.lang.String in Java, but have their own literals and operators.
  • Node is similar to a DOM node, but uses the XPath data model (no DTD/Doctype, no entities, no CData sections, everything normalized)
  • Using the index ([]) operator an XPath expression can be executed on a Node, the result is a NodeList
  • A node has the operators += (add as a child), + (create a node list of the two nodes), -= (remove node from children) and << (replace the node)
  • A NodeList is a list of references to nodes. It has operators like +, += (append a node list) and << (replace all nodes)
  • A normal XML node literal is contained in [[ ]] brackets. To avoid uneccessary escaping, you can use more than two brackets, e.g. [[[[ <element/> ]]]].
  • A perl-string-like XML node expression that allows the insertion of base types is enclosed in single brackets [ ]. This would be a simple node with content: [ <text>Blabla ${somevariable} $anothervariable</text> ] . Variables can be Nodes, NodeLists, Strings, numbers..
  • You can cast any Node to NodeList. NodeLists can be casted to Node, but when the list has more than one member it throws an exception
  • Nodes can be implicitly casted to Strings
  • Strings can be implicitly casted to (text) nodes
  • the keyword prefix is used to define a XML namespace prefix to be used in XML node literals and XPath expressions. It can be used in all places you can declare a const variable, and has the same scoping rules
The example assumes that you are familar with XPath. Dont expect the code to be really useful, it’s just to get a feel for the syntax. I think I could get used to something like this…
class Test {
        prefix ageext "urn:mascot-age-extension"; 

        static void main() {
                Node mascots = [[
        <mascotList>
                <mascot>
                        <name>Tux</name>
                        <species>Penguin</species>
                        <project>Linux</project>
                        <ageext:age>8</ageext:age>
                </mascot>
                <mascot>
                        <name>Konqi</name>
                        <species>Dragon</species>
                        <project>KDE</project>
                        <ageext:age>3</ageext:age>
                </mascot>
        </mascotList> 
]];

                workWithMascots(mascots, 4);
        }

        void workWithMascots(Node mascots, int mimimumAge) {
                mascots[/mascotList/mascot[ageext:age < $minimumAge]] << minimumAge;

                NodeList n = mascots[/mascotList/mascot];
                foreach Node i in n {
                        Node summary = 
[
<summary>${i[name]} is a ${i[species]} and the mascot of ${i[project]}</summary>
];
                        i += summary;
                }
        
                // print all mascots
                int num = 0;
                foreach Node i in n {
                        num++; 
                        Console.println([Mascot Number $num: ${i[summary]}]);
                }
        }
};



2003/12/13
The Linux platform and modern GUI-based operating systems
The Linux platform and modern GUI-based operating systems During the last years I encountered a number of problems in the Linux platform’s architecture that cause problems for GUIs on top of it. When I say ‘the Linux platform’, I refer to the Linux kernel, glibc and the common GNU and Linux tools, but not X11. I also ignore distribution-specific solutions for the problems. In general the lack of a tool is not a real problem for me, it just is some work that needs to be done. I am talking about the architecture, problems that can not be solved without incompatibilities or at least a lot of work. A ‘modern GUI-based OS’ is, for me, a OS that does not require a user to know or use a command line tool, even for rare system-administration tasks. That does not mean that it should be impossible to work with the command line and a text editor, but the command line must not be the only way to do an administration task. So here is the (incomplete) list:
  • A lot of important functionality is only available in command line tools. The code is not available in libraries or anything else that would easily make it possible write another user interface. This includes mounting and unmounting devices (mount and umount commands), copying of files without losing extended attributes (cp, mv etc), file system checking (fsck), changing passwords (passwd), loading and unloading of kernel modules (insmod, rmmode, modprobe etc), network adapter configuration (ifconfig) and modifying of routing tables (route). GUI tools usually try to work around the problem by calling command line tools and then resort to screen scraping for getting their output, or just display the output of the command line tool. The former sucks because there is no formal description of the command line tool’s output and no guarantee that the output does not change in the next version. Screen scraping is ugly in the source code and results in fragile programs. The latter will almost always result in error message that can not be understand by a GUI user, like the KDE’s error message when you try to mount a non-existing device.
  • Almost every file in /etc has its own file format, and for the more complex ones (like Apache’s) this is a real problem. The lack of formal specifications or libraries for these formats are major show-stoppers for GUI administration tools. One reason is that it’s a huge effort to write parsers that do not break when the user edits the files with a text editor, and most of the configuration files were not designed to be edited with a GUI. There’s also the question what happens when a new version adds features like new keywords to the format. All in all the configuration issue is a nightmare, and can only be solved when either there’s a standardized syntax with schema description for the configuration files, or all servers ship either with configuration libraries or GUI tools. Until then the Linux platform won’t be as easy to configure as, for example, most Windows-servers.
  • The $HOME directory mixes documents (the files a ‘regular’ user may be interested in), configuration files and other data of non-file centric applications, like the ‘$HOME/Mail’ directory. In general application-internal files should not be shown to the user who may not understand them, wonders where they come from and, in the worst case, may delete them. KDE currently hides most application-internal files in ‘$HOME/.kde’ and adds a ‘$HOME/Documents’ and a ‘$HOME/Desktop’ directory, which would be a fine solution - if it would be used. Right now even KDE does not use them, the default directory of the file selector is $HOME. And, of course, all other applications do the same. This is not really a architectural problem, but a convention that would require a huge number of application to be changed to default to the $HOME/Documents directory.
  • The /dev directory is a relict from a time when devices were dump and you identified them by the port you connected them to. With newer busses like USB and Firewire this is not possible anymore. They allow you to chain devices or to use hubs to connect several devices to a single port. Furthermore users expect devices to be found even when they connect them to a different port. Modern USB devices can identify themselves with user-readable description, and some of them have a unique serial number so you can even recognize them when you have two identical devices. This makes the /dev structure obsolete for anything but a temporary device name. You can’t save a /dev path and assume that the next time the application is started it is still valid. The Linux drivers increase the device number each time a new device is added. Instead you need a combination of the device’s vendor and model id, its serial number and, as a last resort, its last location in order to find a device that a user has selected. There are some proposed solutions for these kind of problems, but currently you just have a lot of trouble when using more than device of a kind (e.g. two USB printers or joysticks). There’s no standard for browsing the devices and for remembering them after a restart.
  • There is no support for managing centralized user accounts. You can store user accounts on a NIS, Kerberos or LDAP server and authenticate yourself on the client, using PAM and nsswitch.conf. But there is no good way to change them, beside command line tools like passwd and chfn. And they are not extendable to support new authentication mechanisms like Kerberos, each of them comes with its own administration tools. Even if a GUI application would be able to support these special administration tools, the glibc framework does not offer a way to find out where the user account comes from. Unless the user knows where the account is located (or the application has been pre-configured), a GUI application has no chance to allow administration of the user’s account. It’s a far way to a integrated user management system like Active Directory offers…
  • When a /etc/init.d script fails on boot nothing happens but some text that is printed on the console. There’s no way a GUI tool could notice it or even generate a helpful message when the user logs in or while the display manager is running. In general the control over daemons on the system is pretty poor. The only standardized interface is syslog and a couple of scripts that you can use to start and stop the daemons, provided that you screen-scrape them and don’t bother to expose the user to their unfriendly error messages. There’s no way to show which daemons are actually running, some standardized way to configure them, or being noticed when a daemon crashes/stops. The whole boot process and /etc/init.d is quite unusable for a GUI system.
  • Somewhat similar to the /etc/init.d issue is the list of running processes. Sooner or later every user comes in contact with hanging processes, and most of them learn how to kill them. But on Linux all that you get is a list of raw process names that do not have much in common with the actual applications. It may list processes like Apache 1.x that have a dozen instances, it lists sub-processes started by the application, the processes may have names that have not much in common with the actual application name (the ‘krfb’ process name of KDE’s Desktop Sharing is a particulary bad offender) and so on. Other systems show a relatively simple list of actual application names or window names, but Linux does not have any structures that have a higher level than the process, leaving the user exposed to the internals of all running processes.
Together these points are probably a significant part of the reasons why Linux-based systems appear less friendly to a user who is used to a system like Windows or MacOS. These systems have been designed for a GUI from the ground up. On Linux the GUI is an afterthought, and large parts have still not been adapted to be friendly for GUI users.



 

This blog is my dumping ground for thoughts and ideas about Eek. Someday Eek will be a programming language and system, somewhat comparable to Java in scope. It is my attempt to bring sanity to the world of computing.
At least I hope so. Right now it is far from being finished and I can't guarantee that it ever will be. I am still working on the specification, but I won't release anything before I got my first prototype running. The world does not need more vapourware and unusable beta-software. All publicly available information about Eek is contained in this blog. You can find the latest summary here.
This page is powered by Blogger. Isn't yours? Creative Commons License
This work is licensed under a Creative Commons License.