Monday, April 04, 2005
Corporate Personhood
Corporations are disproportionately powerful, because they concentrate huge amounts of money and therefore political influence. Is this good or bad? On the one hand, "what's good for GE is good for America." On the other hand, corporations by law must pursue no objective over maximizing shareholder value. This means externalizing costs whenever possible. For example, a corporation sells 100,000,000 widgets whose use will kill 100 people. That corporation may not legally settle for a smaller profit in order to make its widgets safer, and thereby save the 100 people. (The "what's good for GE" proponents may counter that the public sector makes such cold-hearted calculations all the time, and that the economic activity of the widgets is part of the "rising tide that lifts all boats," making the whole society wealthier and safer.)
What does this have to do with "corporate personhood?" Corporations concentrate huge amounts of capital, but they defuse almost all responsibility. When a corporation makes a million dollars, that money is shared by all its owners. When a corporation kills a person, that guilt is claimed by no one.
Though a corporation is fundamentally no more than a financial partnership, the existence of the corporate entity insulates its officers from morality and from the law. A bad action could spring from the combined actions of many members of a corporation, though none of the individual actions is bad. Even when a single member performs a bad action on behalf of the corporation, it's difficult to detect and prosecute. This insulation itself encourages bad behavior, because members' responsibility to the corporation is more pressing than their responsibility to the society and its laws.
Basing the rights of the corporation on the rights of the person gives corporations additional power. The law gives individuals various rights to protect them from the power of the state (e.g. the Bill of Rights). Sharing these rights with corporations gives them even more power. Treating people and corporations the same allows using a rhetoric of "freedom" to weaken and eliminate regulation, when regulation is the best tool for making corporations consider important externalities, and the only way to correct market failures.
Friday, April 01, 2005
Emulating maxdepth for Solaris find
GNU find can do this with "-maxdepth 1", but Solaris find can't. To get the same functionality for Solaris find, you can use this hack:
find /some/path/. \( -type d -a \! -name . -prune \) -o -type f -print
That is, first prune all directories that aren't named ".", then print the names of the remaining files. It's important that the path be "/some/path/." instead of "/some/path", because that's what enables special treatment for the top level directory.
Thursday, March 31, 2005
Partial Replacing Lambda?
partial(operator.add, 1)
isn't such an attractive substitute for lambda x: x+1
, but that's more about operator
than it is about partial()
.Maybe
partial()+1
should be supported using operator overloading. It'd be especially nice to be able to do partial()+x+y
instead of partial(operator.add, x+y)
This form should probably be a differently named built-in, but built-ins aren't cheap, and the no argument constructor isn't otherwise meangingful.
Wednesday, March 23, 2005
Humble and Smart
This isn't just philosophy; when people know nothing about a thing, they assume that there's "nothing to it". Then they confidently make decisions based on incomplete information. (It could be because interesting elements exploit the interplay of the simple and the complex. Or it could just be because small details aren't visible at a distance.)
Paradoxically, bright people are more susceptible to this thinking. Bright people are accustomed to quickly understanding ideas outside of their field of expertise, and this understanding is often superior to experts'. A bright person can start to believe (subconsciously) that much of the work in a field is characterized by lack of understanding.
This has ramifications for religious leadership. That religious leaders have insight into issues not technically within the sphere of religious practice is a doctrine of Judaism, and probably other religions as well. It's only logical; following Judaism is about living wisely, and someone with a deeper understanding of Judaism should have attained greater wisdom. In order to be truly wise, however, a person must be aware of his limitations. Maybe this awareness can be achieved by encountering sufficiently dramatic examples (for example the abuse cases involving religious leaders in recent years), or maybe it requires learning a little bit about alot of different things.
(It is troubling to think that G-d confers special wisdom in a way that is completely independent of the natural working of the world. For one thing, thinking that you've got supernatural aid from G-d certainly exacerbates the problem described above. For another, there is no good way to tell when you got it, and when you don't. "Trust in God, but steer away from the rocks", applies here as well.
At any rate, I don't think Judaism encourages completely ignoring the rules of the physical world. There's a gemara in niddah that gives advice about achieving various goals; in each case the gemara advises both acting according to natural way of the world, and also praying for success. In this era characterized by hester panim ("G-d hiding His face"), we certainly shouldn't abdicate the responsibility to do our hishtadlus (effort), especially where other people are involved.)
Friday, March 18, 2005
Idiomatic Python
The idiomatic style of python is beautiful.
Here's an example. You'd like to allow categorizing emails using their subjects, by prefixing the normal subject with the category and a colon. So for a message with subject "game on tuesday", you could use subject "sports: game on tuesday". If no category is specified, you'd like to use "misc". Here's the Pythonic way to do it:
try:
category, subject = headers['Subject'].split(':')
except:
category, subject = 'misc', headers['Subject']
There are a few different features at work here:
- "It's easier to ask forgiveness than permission"
- Exceptions are used heavily
- Language support for collections is simple and easy
Friday, March 11, 2005
"Startups" and Small Businesses
It could be that when Graham says "startup", he means a company that would rather flop than settle for staying small. Only talking about these startups is strange for a few reasons. Many companies consider themselves successful without making a public stock offering, or being bought out for piles of cash. How about creating a successful small company first, on the way to becoming an industry giant later? And which ideas and strategies are appropriate for which goal? These questions are a lot more relevant today than how to handle a Venture Capitalist imposed CEO.
After all, though more people are employed by big companies, there are many more small companies than big ones.
Wednesday, March 09, 2005
Note to Future Self
Hopefully when I'm older I'll look back at this note and realize that I'm not going senile.
Tuesday, March 08, 2005
Ipod Shuffle for Lectures
The IPod Shuffle should remember the current "song" after it's turned off.
So apparently this is supported for purchases from ITunes and Audible. In order to get it to work with anything else, you have to use faac to create .m4b files.
UPDATE: It works great. If you're using linux, get gnupod, mpg321, and faac. Then you can do something like:
mkdir /mnt/ipod 2>/dev/nullIncidentally, it would be great if gnupod would expose a filesystem-type api to the ipod using fuse.
mount /dev/sda1 /mnt/ipod
for mp3 in *.mp3
do
m4b=${mp3%.*}.m4b
mpg321 -w $mp3 | faac -b 10 -c 3500 - -o $m4b
gnupod_addsong.pl -m /mnt/ipod $m4b
done
mktunes.pl -m /mnt/ipod
umount /mnt/ipod
UPDATE#2: Don't use gnupod; use the iPod shuffle Database Builder.
Thursday, March 03, 2005
Warn of Unsaved Changes Javascript
<body onLoad="lookForChanges()" onBeforeUnload="return warnOfUnsavedChanges()">
<form>
<select name=a multiple>
<option value=1>1
<option value=2>2
<option value=3>3
</select>
<input name=b value=123>
<input type=submit>
</form>
<script>
var changed = 0;
function recordChange() {
changed = 1;
}
function recordChangeIfChangeKey(myevent) {
if (myevent.which && !myevent.ctrlKey && !myevent.ctrlKey)
recordChange(myevent);
}
function ignoreChange() {
changed = 0;
}
function lookForChanges() {
var origfunc;
for (i = 0; i < document.forms.length; i++) {
for (j = 0; j < document.forms[i].elements.length; j++) {
var formField=document.forms[i].elements[j];
var formFieldType=formField.type.toLowerCase();
if (formFieldType == 'checkbox' || formFieldType == 'radio') {
addHandler(formField, 'click', recordChange);
} else if (formFieldType == 'text' || formFieldType == 'textarea') {
if (formField.attachEvent) {
addHandler(formField, 'keypress', recordChange);
} else {
addHandler(formField, 'keypress', recordChangeIfChangeKey);
}
} else if (formFieldType == 'select-multiple' || formFieldType == 'select-one') {
addHandler(formField, 'change', recordChange);
}
}
addHandler(document.forms[i], 'submit', ignoreChange);
}
}
function warnOfUnsavedChanges() {
if (changed) {
if ("event" in window) //ie
event.returnValue = 'You have unsaved changes on this page, which will be discarded if you leave now. Click "Cancel" in order to save them first.';
else //netscape
return false;
}
}
function addHandler(target, eventName, handler) {
if (target.attachEvent) {
target.attachEvent('on'+eventName, handler);
} else {
target.addEventListener(eventName, handler, false);
}
}
</script>
Wednesday, March 02, 2005
Telephone Shell Javascript Widget
<form>
phone #<input onkeypress="return phone_only(event)">
</form>
<script>
function phone_only(myevent) {
mykey = myevent.keyCode || myevent.which; //ie||netscape
myfield = myevent.srcElement || myevent.target; //ie||netscape
if (mykey == 8) //backspace (netscape only)
return true;
f = myfield.value;
g = myfield.value;
ndigits = f.replace(/-/,'').length;
ngroupdigits = g.replace(/.*-/,'').length;
if (ndigits == 0) {
if (50 <= mykey && mykey <= 57) { //2-9, can't start with 0 or 1
return true;
} else {
return false;
}
} else if (ndigits <= 7) { //only need 2 hyphens: 123-456-7
if (32 <= mykey && mykey <= 47 && ngroupdigits != 0) { //punctuation
myfield.value += "-";
return false;
} else if (48 <= mykey && mykey <= 57) { //0-9
if ((ngroupdigits % 4) == 3) {
myfield.value += "-";
}
return true;
} else {
return false;
}
} else {
return true;
}
}
</script>
Tuesday, March 01, 2005
Forms Support in Javascript, not Template
Here's an example form, followed by the code. You should be able to copy into a file and try it.
<form>
<input name=a>
<input name=b type=checkbox value=x1>
<input name=b type=checkbox value=y1>
<input name=b type=checkbox value=z1>
<select name=c multiple>
<option value=x2>X2
<option value=y2>Y2
<option value=z2>Z2
</select>
<button onClick="populateForm(this.form, {'a':'x0',b:['x1','z1'],'c':['x2','z2']}); return false">Populate</button>
<input type=reset>
</form>
<script>
function populateForm(myForm,myHash) {
for (var k in myForm) {
if (!(k in myForm) || !(k in myHash)) continue;
if (typeof myHash[k] == 'string')
myHash[k] = [myHash[k]];
if (myForm[k].type == 'text') {
myForm[k].value = myHash[k][0];
} else {
var field = 'type' in myForm[k] && myForm[k].type.match('^select') ? 'selected' : 'checked';
var selected = Array();
for (var i=0; i<myHash[k].length; i++)
selected[myHash[k][i]] = 1;
for (var i=0; i<myForm[k].length; i++)
myForm[k][i][field] = myForm[k][i].value in selected;
}
}
}
</script>
Tuesday, February 22, 2005
Even More On Presentation Done Right
Templates are neither necessary nor sufficient for separating model and view (presentation). Not necessary because the separation is really accomplished by the model/view protocol, and not sufficient because presentation code can always seep into the model code. In fact, the StringTemplate approach requires complex views to be implemented in the model code.
A great m/v separation may be achieved by just having separate code for model and for view, even in the same language. The only requirements are on the data passed from the model to the view:
- it should be available before the view code runs
- it should consist only of lists of lists, and lists of opaque objects
It's probably worth noting explicitly that the view depends on the model and not vice versa.
Recent javascript interfaces from google seem to contradict requirement number one, but really they just use a lazy loaded model. The view treats the model as if it's present, but because of the model's prohibitive size, each piece is loaded only just before it's needed.
The StringTemplates author acknowledges that his solution is insufficient to keep view logic out of the model code. The model code could pass data like "the color red" to the view layer. Since StringTemplates is such a simple language, more complex view features must be implemented with the help of model code. Any possible document may be generated, but only with the help of the model code.
So if m/v separation isn't the point of templates, what is? Separation of roles. The roles of programmer and designer aren't necessarily performed by different people, but they do require different approaches. The designer role includes everything that can be done in a WYSIWYG editor, with all visible elements, "includes", and conditionals. As document formats evolve, this could become part of the normative editor functionality. CSS visibility could be hacked to support conditionals, and XInclude takes care of the rest.
Sunday, February 20, 2005
Google Maps and Free Source
A yet un-remarked-upon feature of these "network applications" is how similar they are to free source. Though the intellectual property restrictions of google and of free source are just as strict as those of the most proprietary software vendor, the implementations of both google maps and GPL'ed code are available for anyone to poke at. GPL developers have so much faith in their model that they don't worry about commercial competitors, and google developers apparently have so much confidence in their own skills that they don't worry.
You already have free access to both the google maps client software and the map data. Without too much difficulty, you could extend either. For example, you could use the google client to view your own map data, you could run your own software on the map data (which is already cached on your pc), and people are already hacking new features onto the whole google maps solution.
The major change in these new applications is that the interface is exposed. With regular google search, you either have to use the Google Web APIs or else screenscrape the html pages. With a little reverse engineering of the new applications, you can access the same interface that's used as part of their normal functioning.
(The Google Web APIs limit developers to 1000 queries a day, and presumably a higher volume of screenscraping or access to the new APIs would get shut down by Google.)
The Internet Changing Everything
- Traditional categories of media become irrelevant
Does a given piece of information belong in a book, an academic paper, a weekly magazine article, an editorial, a letter to the editor? The internet makes the distinctions increasingly irrelevant.
- The description of an idea may be canonicalized
A web page is an expression of an idea that is universally available (at least theoretically). It's easy to refer to, and easy to maintain. The internet will one day provide a distributed wikipedia, in which every idea has a URI and page(s) containing all public human knowledge of that idea.
- The context of an idea may be completely specified
Traditional bibliographies are klunky in comparison. It will be possible to link a given piece of information with every other piece of information on which it relies. That metadata will itself be data, subject to easy analysis.
Thursday, February 17, 2005
URL Features
"URL" means both local and remote. I should have a full history of every file I've opened (explicitly), even if it wasn't using the browser. Another operation that I want recorded is when I change files, and when I post forms.
I should get a smarter handling of "cached" and downloaded files. Browsers already have caches, so why I should have to manually make copies of files for speed or reliable access?
Thursday, February 10, 2005
Perl Problems
1 Obscure Features
The core language has so many features that even the greatest perl wizard does not know them all. Find a wizard and ask him how many of the following features from the core language he understands (without consulting the manual).
split(' ') vs split(/ /) vs split('') vs split(//)
$x{'a', 'b'} vs @x{'a', 'b'}
$[ = 3
[{foo => 1, bar => 2}, "FOO", "BAR"]
And here's a realistic example of idiomatic perl:
sub with_gs_acct_check_digit{"$_[0]-".substr(10-(map$_[2]+=$_,map/\d/?$_[1]++%2?split'',$_*2:$_:0,split'',$_[0])[-1]%10,-1)}
Since these features usually address a real need, they can be very tempting to use. Even if someone were to do the work to specify a sane perl subset, there is no tool to enforce its use. Often a perl expert isn't aware that the language features that he uses are obscure or difficult for the nonexpert to understand.
Perl's fast feature accretion is a direct result of its core design philosophy, "There's More Than One Way To Do It".
2 Few Errors are Caught by the Compiler
Since every new feature is encoded using a fixed set of operators, very few errors actually cause a compiler error.
print "Strings are equal\n" if "abba" == "baba";
2.1 Context Confusion
Every perl expression has several entirely different results depending on its "context", the code in which the expression occurs. The following example will trip up even experienced perl coders:
sub myPrint($) {
my $arg = @_;
print "the first argument is $arg ?\n";
}
3 Unsafe Features
Many commonly used features are unsafe. The "use strict" directive only catches the most blatant.
3.1 $_
For example, one of perl's major conveniences is the $_ short-hand for referring to the current index of a loop. The following is great way to print upper-cased versions of some strings.
@t = qw( ibm dell msft );
for (@t) {
print uc "$_\n"
}
However, if you want to instead use a different function in place of "uc", this code may fail in mysterious ways. Functions called in the body of the loop can alter what the $_ reference refers to, and neither the caller nor the callee may realize the danger.
3.2 Exceptions
Exception support is inconsistent and dangerous. When most function fail, they just return false and sometimes an error code. Other functions expect to be called in "eval blocks", and will otherwise cause the entire program to terminate (e.g. mkpath from File::Path)
3.3 Auto-vivification
Another example is auto-vivification, or the on-the-fly creation of datastructures. For the following data structure:
use strict;
my %myhash1=( "a"=>1, "b"=>2 );
my %myhash2 =( "myhash1"=>\%myhash1, "c"=>3, "d"=>4 );
Perl will properly catch the error:
$myhash3{c}++; # %myhash3 is not defined
But will happily allow:
$myhash2{myhash3}{c}++; # a new hash will be created!
4 Data Structures
Nested data structures in perl are unwieldy. They require using references, and most people are confused by references.
Wednesday, February 09, 2005
Reusing Software
There are still a few impediments to reuse in popular platforms. The following is a small collection of research into these impediments.
There mustn't be conflicts between new symbols added to the base code and the extensions. It wouldn't be difficult for languages to support this better.
The non-private call graph of public and protected methods in the parent mustn't change.
There must be a way to better encapsulate objects.
There must be a way to encapsulate and compose concurrent access to state.
Thursday, January 27, 2005
Software Commodification
A commodity is any product which meets some agreed upon criteria, such that any instance of that commodity may be substituted for any other. A quart of milk from any supplier will satisfy your cupcake recipe, no matter who produces it, so milk is a commodity. A necktie, however, is subject to all sorts of weird style criteria, so it is not a commodity. It might not even be your color.
All software programs are commodities in essence. Programs follow logical rules and perform to a specification, but we can't say a given program is a commodity before it has an actual substitute. So commodity nature is revealed when a given piece of software acquires competing alternatives.
Most software alternatives are not perfect substitutes in the sense that you can switch implementations and barely notice. Usually switching requires retraining the people that use the product and porting the software that uses the product. Vendors obviously want to keep switching as difficult as possible, and techniques for doing so are called "lock-in". By definition, strong lock-in hurts the consumer, but it also usually hurts the consumer by creating an inferior product. A product with strong lock-in will generally be monothilic, have complicated interfaces, and have hidden dependencies.
Given a choice, anyone (whether selling software or anything else) would prefer to have their product not to be a commodity. This is because markets are especially efficient at setting commodity prices, and this means suppliers will have stiff competition.
It's worse to have your software product become a commodity. This is because of the nature of software (and any non-physical product). The first copy of your software will cost you $2,000,000 but every subsequent copy will cost you only $2. This means that when someone enters your market, they've already done the hard part, and there's no incentive to leave. More importantly, the price of a software product is completely arbitrary. A price war could bring prices all the way down to zero.
Free competitors may be particularly formidable. Only with software is free competition realistic, because with physical products, revenue must reliably exceed costs. Free software makes it particularly easy to pool contributions from disparate sources, with very little administrative overhead, and very little reliance on a single controlling entity. (If a maintainer isn't doing a sufficiently good job, then someone may "fork" the development effort.) Free efforts also generally aren't motivated to lock-in.
All things being equal, it's desirable for software vendors to have the other software that they depend on to be commodified. This is for a few reasons: your dependencies have power over your product, and when your dependencies are cheaper, your overall product solution will be cheaper. There'll be a larger market for your product.
So if free software is so easy and desirable, how can a commercial software vendor stay in business? By presenting a moving target. Any given software release may be straightforwardly commodified, but the release process may not be. No entity or group of entities will have external incentive to track a series of upgrades. This is partially why "tying" is so important to a large software vendor, because tying is the only way for a mature product to escape commodification.
Ironically, the vendor is really in the business of selling timely upgrades, a service business which uses the base software product as merely a barrier to entry.
Wednesday, January 26, 2005
Intuitive Interfaces
- It works like other similar interfaces that the user has seen. (Green means "go".)
- It's logical. (Cars' signalling interface correspond to the way that you turn the wheel.)
- It's internally consistent. (The same abstraction works the same way in different contexts.)
- It has a shallow learning curve.
- It has helpful pointers, ideally inobtrusive. (Examples are popup help bubbles and pie menus)
- It allows safe experimentation. (You can "undo" any operation.)
- It's simple. :)
Python Evolution and AOP
For a while, python has had metaclasses, which allow customizing the behavior of whole classes of classes :). These metaclasses allow running code on class creation, and routinely involve customizing object creation.
In order to obviate java's setter/getter pattern, python has the ability to run code on getting an attribute or setting an attribute or both.
Recently, python attained a decorator feature, which allows running code on function/method creation. This feature was originally motivated by wanting to specify things like a method being "static".
Now there's talk about implementing type-checking use adaption of function/method arguments and return values.
This trend could seem a lot like Aspect Oriented Programming, but it has the important difference that the path to the hook code is always evident at the hooked code. A main point of AOP is perfect "obliviousness", according to which you can alter the behavior of foreign code without making any modification to it. In python, the main code body can be oblivious of the hook, but it's always preceded by an explicit reference to the hook, almost like an annotation.
In python, each of these hooking mechanisms has a different syntax, presumably tailored to its primary use case. These features are mostly made possible by python's easy introspection support, and by its uniform "object" treatment of all of these abstractions.
Monday, January 24, 2005
Managing Change
Obviously, don't remove public and protected methods, or change their semantics. For protected methods, this includes having them continue to call other nonprivate methods under the same conditions.
Less obviously, if you're creating new public or protected methods, you should move your code to an entirely new class! Someone may have extended your class with a method that has the same signature that you've just added, causing a needless conflict. Under the old class, create proxy classes that delegate every method to the new code. You don't have to worry about the maintenance overhead, because these proxies represent the legacy interface; you shouldn't have to change them later.
This hints at the fact that your library should be very careful about calling its own nonprivate methods. Calling internal nonprivate methods should be reserved for supported abstractions, because your library will down-call into extensions of these methods. If you don't want to support the abstraction, move the implementation into a private method. Have your internal code call the private method, and have your public method just be a wrapper for clients.
This approach is not so elegant because it requires code changes in order to support unchanging function. Maybe languages should support using interfaces for encapsulation, so that any symbols not present in the specified interface(s) would be completely hidden. However, all the recent python typechecking and adaption thinking seems to be focused on method calls, not base class specification.
Thursday, January 06, 2005
Minimum Curriculum
Even if this makes sense for ideas, it certainly makes no sense for art. The survey courses in dance, art history, music history, and world literature probably can't be meaningfully condensed any further. But no one should miss out on the major ideas of the introductory courses in math, astronomy, physics, chemistry, biology, psychology, economics, computer science, philosophy, political science, philosophy, and history. Some ideas fall through the cracks of all of these intro courses. (For example, what about the central points of The Death and Life of Great American Cities?) Some ideas are also too new to make it into conventional courses. For many ideas, only a few minutes explanation would be sufficient.
Many survey courses take the historical approach, but in this context it would waste too much time. To make it more interesting, the course (and more importantly the reading list) could touch on the limits of human knowledge and current work.
I originally had this idea in yeshiva, in a spirit of parochialism: "take this course, and then forget about the rest." Ironically, the course would probably not be popular with the insular (even if it were to omit evolution). This idea came back to me recently, thinking about John Rawls. I realized that neither my college survey in humanities nor intro to philosophy mentioned him.
The following are the ideas that I could come with quickly; please suggest more.
big bang, scale and elements of the universe, basic probability, limits, idea of calculus, newton's laws, four forces, electricity, nature of light, relativity, phases of matter, entropy, periodic table, cells, virii, bacteria, dna, systems of the body, evolution, milgram study, the unconscious, supply and demand, how a computer works and what a program looks like, urls, kant's categorical imperative, veil of ignorance, a myriad of -isms: utilitarianism, facism/socialism/capitalism(democracy), world religions, epistemology, problem of induction, logic and godel for dummies, mixed-use cities, human history in 30 minutes :), how toilets work
Wednesday, January 05, 2005
G-d Exists, so I could be Wrong
David Myers
Psychologist, Hope College; author, "Intuition"
As a Christian monotheist, I start with two unproven axioms:
1. There is a God.
2. It's not me (and it's also not you).
Together, these axioms imply my surest conviction: that some of my beliefs (and yours) contain error. We are, from dust to dust, finite and fallible. We have dignity but not deity.
And that is why I further believe that we should
a) hold all our unproven beliefs with a certain tentativeness (except for this one!),
b) assess others' ideas with open-minded skepticism, and
c) freely pursue truth aided by observation and experiment.
This mix of faith-based humility and skepticism helped fuel the beginnings of modern science, and it has informed my own research and science writing. The whole truth cannot be found merely by searching our own minds, for there is not enough there. So we also put our ideas to the test. If they survive, so much the better for them; if not, so much the worse.
Tuesday, January 04, 2005
Core Assumptions
Wouldn't it be great if all disagreements could be reduced to conflicting core assumptions? If the parties could agree on this reduction, then maybe the assumptions themselves could be meaningfully discussed and compared.
Of course, I'm thinking of the electoral split of recent times. Could it be that conservatives simply don't accept the idea of the Veil of Ignorance?
Beating Java on Its Own Turf
This is exciting because it promises to achieve the safety of java with the simplicity of python. Safety is one of the three technical reasons that people still use java instead of python. The other two are performance and enterprise software. Performance is being addressed by the newly funded PyPy. Python already has much of the java enterprise technology, just not in the form of ballyhooed specifications. (For the remaining technologies, it's not clear that the "enterprise solution" achieves better reliability or scalibility than folks' nonstandard solutions.)
That the business community seems to care more about these specifications than about the actual technology may be for the same reason that it doesn't greatly prefer Sun's specifying Java over Microsoft's owning .NET. (Without a patent grant, the incomplete .NET standards work is almost worthless.) One would think that having one company write specifications for which many companies can build implementations would be much safer, and would create much more open software. That the wholely proprietary .NET is even considered a contender implies that people only care about the company that writes the interfaces.
Monday, December 27, 2004
Special Characters and Gmail
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
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
- a persistent store
- a type system
- indexing features
- a transaction manager
- a query language
- a network protocol
- usually a full embedded programming language
One Device
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
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
Thursday, December 16, 2004
Note about Gnome Storage
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
- create a group "remote", and add users to it that may login remotely
- add the following lines to your /etc/security/access.conf:
+:remote:ALL
-:ALL:localhost
+:ALL:LOCAL
-:ALL:ALL - add the following line to your /etc/pam.d/system-auth:
account required /lib/security/$ISA/pam_access.so
- add the following line in your /etc/pam.d/su:
auth required /lib/security/$ISA/pam_wheel.so use_uid deny group=remote
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=failand 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
Wednesday, December 08, 2004
Gcc on Solaris
/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
- Everyone in your addressbook can mail you without qualification
- Everyone can respond to one of your private messages
- Everyone can respond to one of your public messages within a fixed interval of time
- Everyone else can mail you by confirming their first message by email
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
No one should ever have to "save" a file. This requires a few things:
- Unlimited undo and redo
- Files should only be named with UUIDs
- Files should be easily searchable using metadata
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
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
- Windows should never overlap. Windows overlap when one partially obscures another.
- Windows should only ever be grouped by tabs or tiles.
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
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 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
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
- Get the current page via the google cache
- Refresh the current page every five minutes until the little window is closed
Friday, November 12, 2004
Making Things Easy to Maintain
- Make it as simple as possible.
- Use test driven development.
- Keep as little state as possible.
- Centralize environment configuration.
- Make jobs rerunnable/idempotent whenever possible.
- Give temporary resources short-lived unique names.
Tuesday, November 09, 2004
Note About Dune
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.
Thursday, November 04, 2004
On G-d's Side
The root cause is that when you decide (on some level) to do the right thing, you get comfortable with the idea that you are doing the right thing.
Believing that you are doing the right thing can make you arrogant, and it can make you intolerant of people who are doing different things. Believing that you are doing the right thing can cause you to compromise for your own benefit. It's a case of the ends justifying the means, but it's more seductive. You feel that those who are doing good should be enabled to continue doing good. But if you ever flag in your devotion, you're also guilty of hypocrisy.
The converse danger is paralysis. If you refrain from judging, there's no basis for choice. All the choices that you do make will be weakened.
Maybe we can just continue to act on our consciences, so long as we realize how little we understand the connection between our efforts and their actual effects.
Wednesday, November 03, 2004
2004 Election Issues
of the people who considered each issue most important, how many of them voted...
| overall | for bush | for kerry |
taxes | 5% | 57% | 43% |
education | 4% | 26% | 73% |
iraq | 15% | 26% | 73% |
terrorism | 19% | 86% | 14% |
economy/jobs | 20% | 18% | 80% |
moral values | 22% | 80% | 18% |
health care | 8% | 23% | 77% |
also, people who voted for bush overwhelmingly thought that the economy is doing well, and that we should have invaded iraq. people who voted for kerry overwhelmingly thought the economy is doing poorly, and that we shouldn't have invaded iraq.
Categories of Chumra
Ever since the disbanding of the Sanhedrin (central government of rabbinic judaism), religious legislation has been somewhat up in the air. This situation leads to the most fashionable type of chumra, the attempt to satisfy all the conflicting decisions made by a group of different decisors.
The group of decisors is often the rishonim (medieval scholars), and satisfying their decisions inspires many of the famous "Brisker Chumros". These chumros are more compelling when the issue is a matter of Biblical law rather than Rabbinic, but often the provenance itself is also in question. It is literally impossible to always satisfy all of the decisions of all the rishonim. [To be written: rashi and rabbenu tam tefillin example, rov example]
As we consider later decisions, it becomes increasingly less clear whether a given position is a chumra or actual halacha. At least for opinions of the rishonim, we usually have a idea of the halacha from the Shulchan Aruch. After that, we often have many different strong positions and no agreed upon principle for deciding amongst them. These questions are usually either less fundamental, or else they deal with technology and new situations.
A subcategory of satisfying all decisions is to observe authoritative decisions that are mostly disregarded. This disregard may be explained away by later authorities, or it may be mysterious. Either way, we are reluctant to suppose that the majority of observant jews are violating halacha, so these decisions take on the status of chumra. An example is Ezra's decree that after seminal emission, men should immerse in the Mikveh before learning or praying.
Another major category of chumra is that of ascetic practices. In general, the torah encourages--and perhaps requires--embracing life, but historically scholars have valued greater restraint. Maimonides famously praises the ideal of moderation, but his view of moderation looks harsh by today's standards. An example is fasting most Mondays and Thursdays.
Excruciating attention to detail is another major form of chumra, which some consider to be part of normative halacha. The classic example is that halacha specifies the proper order for tying your shoes. This trend goes back to the Talmud, which wrangles over the exact right place to first break open a loaf of bread. People may try to be meticulous in interpersonal relations as well (for example, not telling a companion at a restaurant that you dislike your meal, lest the waiter overhear and get hurt feelings).
Many of these chumras evoke awareness of G-d and godliness in every aspect of one's life, even the most picayune. It could be that accomplishing this awareness is more important than the actual details themselves.
Mysticism informs mainstream halacha, but it also forms a rich body of chumra. Presumably this category need not be limited to the traditional literature, but may include fringe texts, and even personal research into hidden worlds.
Much of civil law is about compromise, where neither party to a dispute is actually at fault. The Talmud describes "chasidus" as the habit of forgoing legal advantage in many such situations, and exceeding one's pecuniary obligation. [Source in bava kamma]
The last category of chumra is a biggy: do more good. This means strengthening the pillars of the world: learning more torah, giving effort and money to those in need, and praying more meaningfully.
Friday, October 29, 2004
Tradition
Why should that be?
Making tradition so central (and not just a particular tradition, but the institution of tradition itself), emphasizes the notion of the Jewish People as an entity, an organism.
Just as a person grows throughout his life, and interacts with his environment, so too, we are to imagine the Jewish People moving through history.
Wednesday, October 27, 2004
Overdue Library Book Notifier
#!/usr/bin/python
#mail notification of due books on libraries using the Dynix Information Portal
#todo: renew each that is due, and mail only if fail
import re, time, smtplib
from urllib import urlopen, urlencode
from datetime import datetime, date
days_warning = 4
excluding_weekends = False
accounts = (
("1 2345 67890 1234","1234"),
("9 8765 43210 9876","9876"),
)
fromaddr = "somebody@somewhere.com"
toaddrs = ( "somebody@somewhere.com", "somebodyelse@somewhere.com", )
headers = [ "Subject: LIBRARY BOOKS DUE", ]
url = "http://leopac4.nypl.org/ipac20/ipac.jsp"
params = {
"menu":"account",
"aspect":"overview",
"profile":"dial--3",
"button":"Login",
}
itemsout_re = re.compile("href=.*?uri=.*?>(.*?)</a>.*?(\d{2}/\d{2}/\d{2}).*?(\d{2}/\d{2}/\d{4})")
trailing_nonword_re = re.compile("\W*$")
itemsdue = []
for params["sec1"], params["sec2"] in accounts:
a = urlopen(url+"?menu=account") #get session ticket
params["session"] = session = re.search(r"session=([\w\.]+)", a.read()).group(1)
b = urlopen(url, urlencode(params)) #do login
c = urlopen(url+"?session="+session+"&menu=account&submenu=itemsout") #get book list
html_itemsout = c.read()
today = date.today()
if excluding_weekends and (today.weekday() + days_warning) >= 5: #saturday
days_warning += 2
for i in itemsout_re.finditer(html_itemsout):
due = datetime(*time.strptime(i.group(3),"%m/%d/%Y")[:6]).date() - today
if due.days < days_warning:
itemsdue.append((trailing_nonword_re.sub("",i.group(1)),due.days))
if itemsdue:
msg = "\n".join(headers+[""])
for i in itemsdue:
msg += "\""+i[0]+"\" is due in "+str(i[1])+" days\n"
smtplib.SMTP('localhost').sendmail(fromaddr, toaddrs, msg)
Wednesday, October 20, 2004
Exceptions
- Contract not met by client (either code or supporting configuration)
- Resource not available
- Programming error in provider code
- Try to fix it
- Reset state
- Address the issue specified by the exception
- Reset state
- Just do without the functionality
- Present the error
- Log it
- Report to the user
- Throw early
- Catch late
- Handle exactly once
- New exceptions are for new ways of handling
- Use "finally" to do cleanup
- Don't throw in constructors
If an exception can't be handled differently based on its type, you shouldn't create a new exception class for it.
With java 1.4 exception wrapping, there's no need to have separate exception classes for separate components.
Inspired by O'Reilly's Best Practices for Exception Handling.
Monday, October 18, 2004
Better Programmers' Editor
while(true){i+=1;}
the editor should immediately display:
while (true) {Cursor movement should also ignore that whitespace. (If the cursor is before the "i" above, pressing the left arrow should move the cursor to before the "{".)
i += 1;
}
Of course, the editor should also wrap long lines as you type.
This behavior is described in Displaying and Editing Source Code in Software Engineering Environments, but I haven't seen an implementation.
Just found Harmonia, which looks like it does it. An eclipse plugin is in the works.
Thursday, October 14, 2004
What Politicians Don't Talk About
For example, I don't think anyone disputes that a strong motivation for invading Iraq was fear of unfriendly power over America's interests, namely its interest in cheap oil. (Why not Libya?) Presumably there's a good political reason why Kerry also doesn't mention this.
It goes the other way too; much of what politicians talk about is completely irrelevant.
Besides being highly cynical, what's wrong with this state of affairs? It insulates much of the electorate from political reality, but you could argue that everyone would be almost as insulated anyway.
Are the members of one political party disproportionately guilty of this disingenuousness?
Monday, October 11, 2004
Spaces vs. Tabs
- simplicity
- columns may be arbitrarily aligned
- xterm copying doesn't need special handling
- yaml doesn't need special handling
- backspace and navigation work easily
- number of displayed spaces is easily customizable
- indentation semantic is represented directly, and already standard
- makefiles don't need special handling
Arbitrary Alignments using Hard Tabs
With hard tabs, you can't portably line up your comments or constants like this:doSomething(); # this is a silly block of code, doSomethingElseWithAMuchLongerStatement(); # which has a multi-line comment, # which extends past the code itselfAll but the third line could easily be solved by using spaces for tabs that occur after non-whitespace on each line. Even the third could be excepted because it doesn't involve control flow. This motivates some people to recommend a hybrid "use tabs for indentation and spaces for alignment", and others keep it simple by recommending "use tabs for indentation and eschew alignment". Eschewing alignment could involve using:
mylist = [ 1, 2, 3 ]instead of:
mylist = [ 1, 2, 3 ]
All this can be tricky, and reveals the most important advantage of soft tabbing: it's What You See Is What You Get, easily confirmable by checking that files contain no hard tab characters. It's harder to confirm that alignment isn't messed up when using hard tabs.
Xterm Clipboard using Hard Tabs
Copying from an xterm converts hard tabs to soft tabs, so you have to configure your editor to convert them back.Navigation using Soft Tabs
Just like a single keystroke creates an indentation level, a single keystroke should move the cursor over an indentation level. Modern editors allow you to back up an indentation level using the Backspace key, but don't yet allow you to navigate indentation levels using the arrow keys.Customizing the Number of Displayed Spaces using Soft Tabs
Though it's certainly possible to munge source code in order to customize the tabstop when you're viewing code, it's a little risky. Tools for some languages (perl) don't make any guarantees that it can be done reliably. Even if you can do it reliably, you'll have to hack it, and if you also make modifications you'll probably cause bogus diff history.Makefiles using Soft Tabs
The configuration files used by themake
tool require hard tabs, so your editor has to handle this.
Semantics
There is some beauty in representing the semantic unit with a single character, and without extraneous formatting information (sort of like "\n" instead of "\r\n").Editor Configuration
vim for soft tabbers:set expandtab | set smarttab
and shiftwidth
should be set to configure the number of spaces to use for indentation; tabstop
will be only be consulted for tabs not occurring at the beginning of the line. In order to handle makefiles, use: au FileType make setlocal noexpandtab
vim for hard tabbers: set noexpandtab
or maybe use a script to expand tabs later in the linePlatform Proliferation
Windows and Mac each have a couple of platforms, as do the free desktops projects (Gnome and KDE), the major cross platform applications (Mozilla and OpenOffice), and most modern "languages" (e.g. Java, Python, OCaml).
People love the UNIX interface, in which everything is a file, and a file is a stream of bytes which may have a hierarchical name. That interface now appears to be insufficiently rich, but it's powerful exactly because it's so simple.
Maybe there's a new interface which is comparably simple, but can do 80% of what we want for components? Some examples of difficult modules to support are: graphical user interface, spell-checking, auto-completion, clipboard and search and undo, linking and embedding documents.
At the Desktop Developer Conference, Havoc Pennington talked about fragmentation of platforms.
Wednesday, October 06, 2004
One True Language
This is a big mistake. For most applications, the benefit of using a specific language is minimal. The cost, however, of having code written in many different languages is huge. Code written in different languages is difficult to re-use. It's difficult to integrate. It's difficult to learn and support. In short, the language that code is written in is its single biggest dependency, and we are increasing and fracturing dependencies for no good reason. For a recent project at work, I had to write in several different languages: Java, Perl, Csh, SQL / Sybase stored proc language, XML, and a proprietary "Job Information Language". A single language could satisfactorily perform the jobs of all, and it would only need a single toolset.
Techies like the idea of a large language ecosystem because they love this stuff. They're attached to it, and want to see more of it. They love languages like shoemakers love shoes.
It almost doesn't matter what the one true language is, but of course I'm going to share some ideas about what I think it should look like.
The C programming language was an abstraction layer for making code portable. The next abstraction layer should make it as easy as possible to write and maintain code. All the other requirements are relatively insignificant.
So the language should be mostly procedural and object oriented. It should have automatic garbage collection. It should have built-in collection classes. It should be dynamically typed, but strongly typed.
The Zen of Python (by Tim Peters)
Beautiful is better than ugly.Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!
Tuesday, October 05, 2004
Property
100 years ago, when you bought a book, you owned that book. You could do anything you wanted with your copy of that book except make a copy of it. The government gave publishers the right to prevent copying their books, in order to maximize the amount of publishing that would be done.
Now, when you buy a book, you haven't bought anything. You've licensed that book. The publisher retains all the rights except for limited reading privileges.
This isn't yet completely true, but it is the way that everyone already thinks of it. The law considers every digital access to be a "copy", so every use is governed by copyright. "Click-wrap" and "shrink-wrap" allow publishers to easily dispense with traditional copyright, and invent any bizarre license conditions they please. This is only logical, since the publisher owns the work.
What's wrong with strict intellectual property? Shouldn't the market discourage unreasonable license terms, like restricting the right to read books aloud?
Well this market is inefficient. Publishers consolidate. Publishers are big, and consumers, in general, are small. This makes it difficult for consumers to negotiate. It's a shame that fair use will suffer, but there are two bigger problems with strong IP:
- it's grossly inefficient
- it could hamper the "prosumer revolution"
The internet makes it so easy to publish that potentially every consumer could become a producer of valuable creative work. Creative work is possible only by building on top of existing work. Clearing rights has become significantly more difficult than distribution. Most lawyers don't see anything wrong with increasing the need for lawyering in the world, but everyone else should recognize that the extra transaction costs are prohibitive.
It's hard to tell how inefficient our current system is, without real systems to compare it to. But it's equally hard to believe that authors are motivated by the potential revenues from their work 100 years from now. Less extreme cases are harder to judge.
With what should the intellectual property metaphor be replaced?
Many thinkers advocate "compulsory licensing", which requires everyone to pay a tax for funding new works. The proceeds from this tax would be divided up and awarded to authors based on the results of some sort of polling. It would be difficult to prevent gaming such a system. It would also be difficult to properly poll for derivative works. It would probably require administration to assign weightings to different types of work.
Alternatively, we could eliminate copyright law altogether, and have publishers just protect new content as best they can. This wouldn't be so different from today, except that content which is eventually freed would then be legal. Books would be the big loser in this scenario, because they are almost impossible to protect. Once comfortable reader devices are available, copying books will become rampant.
see Property, Intellectual Property, and Free Riding and The Darknet Paper
Monday, October 04, 2004
Hallel
ברוך אתה יהוה אלהינו מלך העולם אשר קדשנו במצותיו וצונו לקרוא את ההלל
הַלְלוּ-יָהּ:הַלְלוּ, עַבְדֵי יְהוָה; הַלְלוּ, אֶת-שֵׁם יְהוָה. יְהִי שֵׁם יְהוָה מְבֹרָךְ-- מֵעַתָּה, וְעַד-עוֹלָם. מִמִּזְרַח-שֶׁמֶשׁ עַד-מְבוֹאוֹ-- מְהֻלָּל, שֵׁם יְהוָה. רָם עַל-כָּל-גּוֹיִם יְהוָה; עַל הַשָּׁמַיִם כְּבוֹדוֹ. מִי, כַּיהוָה אֱלֹהֵינוּ-- הַמַּגְבִּיהִי לָשָׁבֶת. הַמַּשְׁפִּילִי לִרְאוֹת-- בַּשָּׁמַיִם וּבָאָרֶץ. מְקִימִי מֵעָפָר דָּל; מֵאַשְׁפֹּת, יָרִים אֶבְיוֹן. לְהוֹשִׁיבִי עִם-נְדִיבִים; עִם, נְדִיבֵי עַמּוֹ. מוֹשִׁיבִי, עֲקֶרֶת הַבַּיִת-- אֵם-הַבָּנִים שְׂמֵחָה: הַלְלוּ-יָהּ.
בְּצֵאת יִשְׂרָאֵל, מִמִּצְרָיִם; בֵּית יַעֲקֹב, מֵעַם לֹעֵז. הָיְתָה יְהוּדָה לְקָדְשׁוֹ; יִשְׂרָאֵל, מַמְשְׁלוֹתָיו. הַיָּם רָאָה, וַיָּנֹס; הַיַּרְדֵּן, יִסֹּב לְאָחוֹר. הֶהָרִים, רָקְדוּ כְאֵילִים; גְּבָעוֹת, כִּבְנֵי-צֹאן. מַה-לְּךָ הַיָּם, כִּי תָנוּס; הַיַּרְדֵּן, תִּסֹּב לְאָחוֹר. הֶהָרִים, תִּרְקְדוּ כְאֵילִים; גְּבָעוֹת, כִּבְנֵי-צֹאן. מִלִּפְנֵי אָדוֹן, חוּלִי אָרֶץ; מִלִּפְנֵי, אֱלוֹהַּ יַעֲקֹב. הַהֹפְכִי הַצּוּר אֲגַם-מָיִם; חַלָּמִישׁ, לְמַעְיְנוֹ-מָיִם.
לֹא לָנוּ יְהוָה, לֹא-לָנוּ: כִּי-לְשִׁמְךָ, תֵּן כָּבוֹד--עַל-חַסְדְּךָ, עַל-אֲמִתֶּךָ. לָמָּה, יֹאמְרוּ הַגּוֹיִם: אַיֵּה-נָא, אֱלֹהֵיהֶם. וֵאלֹהֵינוּ בַשָּׁמָיִם--כֹּל אֲשֶׁר-חָפֵץ עָשָׂה. עֲצַבֵּיהֶם, כֶּסֶף וְזָהָב; מַעֲשֵׂה, יְדֵי אָדָם. פֶּה-לָהֶם, וְלֹא יְדַבֵּרוּ; עֵינַיִם לָהֶם, וְלֹא יִרְאוּ. אָזְנַיִם לָהֶם, וְלֹא יִשְׁמָעוּ; אַף לָהֶם, וְלֹא יְרִיחוּן. יְדֵיהֶם, וְלֹא יְמִישׁוּן--רַגְלֵיהֶם, וְלֹא יְהַלֵּכוּ; לֹא-יֶהְגּוּ, בִּגְרוֹנָם. כְּמוֹהֶם, יִהְיוּ עֹשֵׂיהֶם-- כֹּל אֲשֶׁר-בֹּטֵחַ בָּהֶם. יִשְׂרָאֵל, בְּטַח בַּיהוָה; עֶזְרָם וּמָגִנָּם הוּא. בֵּית אַהֲרֹן, בִּטְחוּ בַיהוָה; עֶזְרָם וּמָגִנָּם הוּא. יִרְאֵי יְהוָה, בִּטְחוּ בַיהוָה; עֶזְרָם וּמָגִנָּם הוּא.
יְהוָה, זְכָרָנוּ יְבָרֵךְ: יְבָרֵךְ, אֶת-בֵּית יִשְׂרָאֵל; יְבָרֵךְ, אֶת-בֵּית אַהֲרֹן. יְבָרֵךְ, יִרְאֵי יְהוָה-- הַקְּטַנִּים, עִם-הַגְּדֹלִים. יֹסֵף יְהוָה עֲלֵיכֶם; עֲלֵיכֶם, וְעַל בְּנֵיכֶם. בְּרוּכִים אַתֶּם, לַיהוָה-- עֹשֵׂה, שָׁמַיִם וָאָרֶץ. הַשָּׁמַיִם שָׁמַיִם, לַיהוָה; וְהָאָרֶץ, נָתַן לִבְנֵי-אָדָם. לֹא הַמֵּתִים, יְהַלְלוּ-יָהּ; וְלֹא, כָּל-יֹרְדֵי דוּמָה. וַאֲנַחְנוּ, נְבָרֵךְ יָהּ-- מֵעַתָּה וְעַד-עוֹלָם: הַלְלוּ-יָהּ.
אָהַבְתִּי, כִּי-יִשְׁמַע יְהוָה-- אֶת-קוֹלִי, תַּחֲנוּנָי. כִּי-הִטָּה אָזְנוֹ לִי; וּבְיָמַי אֶקְרָא. אֲפָפוּנִי, חֶבְלֵי-מָוֶת--וּמְצָרֵי שְׁאוֹל מְצָאוּנִי; צָרָה וְיָגוֹן אֶמְצָא. וּבְשֵׁם-יְהוָה אֶקְרָא: אָנָּה יְהוָה, מַלְּטָה נַפְשִׁי. חַנּוּן יְהוָה וְצַדִּיק; וֵאלֹהֵינוּ מְרַחֵם. שֹׁמֵר פְּתָאיִם יְהוָה; דַּלֹּתִי, וְלִי יְהוֹשִׁיעַ. שׁוּבִי נַפְשִׁי, לִמְנוּחָיְכִי: כִּי-יְהוָה, גָּמַל עָלָיְכִי. כִּי חִלַּצְתָּ נַפְשִׁי, מִמָּוֶת: אֶת-עֵינִי מִן-דִּמְעָה; אֶת-רַגְלִי מִדֶּחִי. אֶתְהַלֵּךְ, לִפְנֵי יְהוָה-- בְּאַרְצוֹת, הַחַיִּים. הֶאֱמַנְתִּי, כִּי אֲדַבֵּר; אֲנִי, עָנִיתִי מְאֹד. אֲנִי, אָמַרְתִּי בְחָפְזִי: כָּל-הָאָדָם כֹּזֵב.
מָה-אָשִׁיב לַיהוָה-- כָּל-תַּגְמוּלוֹהִי עָלָי. כּוֹס-יְשׁוּעוֹת אֶשָּׂא; וּבְשֵׁם יְהוָה אֶקְרָא. נְדָרַי, לַיהוָה אֲשַׁלֵּם; נֶגְדָה-נָּא, לְכָל-עַמּוֹ. יָקָר, בְּעֵינֵי יְהוָה--הַמָּוְתָה, לַחֲסִידָיו. אָנָּה יְהוָה, כִּי-אֲנִי עַבְדֶּךָ: אֲנִי-עַבְדְּךָ, בֶּן-אֲמָתֶךָ; פִּתַּחְתָּ, לְמוֹסֵרָי. לְךָ-אֶזְבַּח, זֶבַח תּוֹדָה; וּבְשֵׁם יְהוָה אֶקְרָא. נְדָרַי, לַיהוָה אֲשַׁלֵּם; נֶגְדָה-נָּא, לְכָל-עַמּוֹ. בְּחַצְרוֹת, בֵּית יְהוָה-- בְּתוֹכֵכִי יְרוּשָׁלִָם: הַלְלוּ-יָהּ.
הַלְלוּ אֶת-יְהוָה, כָּל-גּוֹיִם; שַׁבְּחוּהוּ, כָּל-הָאֻמִּים. כִּי גָבַר עָלֵינוּ, חַסְדּוֹ--וֶאֱמֶת-יְהוָה לְעוֹלָם: הַלְלוּ-יָהּ.
הוֹדוּ לַיהוָה כִּי-טוֹב: כִּי לְעוֹלָם חַסְדּוֹ.
יֹאמַר-נָא יִשְׂרָאֵל: כִּי לְעוֹלָם חַסְדּוֹ.
יֹאמְרוּ-נָא בֵית-אַהֲרֹן: כִּי לְעוֹלָם חַסְדּוֹ.
יֹאמְרוּ-נָא יִרְאֵי יְהוָה: כִּי לְעוֹלָם חַסְדּוֹ.
מִן-הַמֵּצַר, קָרָאתִי יָּהּ; עָנָנִי בַמֶּרְחָב יָהּ. יְהוָה לִי, לֹא אִירָא; מַה-יַּעֲשֶׂה לִי אָדָם. יְהוָה לִי, בְּעֹזְרָי; וַאֲנִי, אֶרְאֶה בְשֹׂנְאָי. טוֹב, לַחֲסוֹת בַּיהוָה-- מִבְּטֹחַ, בָּאָדָם. טוֹב, לַחֲסוֹת בַּיהוָה-- מִבְּטֹחַ, בִּנְדִיבִים. כָּל-גּוֹיִם סְבָבוּנִי; בְּשֵׁם יְהוָה, כִּי אֲמִילַם. סַבּוּנִי גַם-סְבָבוּנִי; בְּשֵׁם יְהוָה, כִּי אֲמִילַם. סַבּוּנִי כִדְבוֹרִים-- דֹּעֲכוּ, כְּאֵשׁ קוֹצִים; בְּשֵׁם יְהוָה, כִּי אֲמִילַם. דַּחֹה דְחִיתַנִי לִנְפֹּל; וַיהוָה עֲזָרָנִי. עָזִּי וְזִמְרָת יָהּ; וַיְהִי-לִי, לִישׁוּעָה. קוֹל, רִנָּה וִישׁוּעָה--בְּאָהֳלֵי צַדִּיקִים; יְמִין יְהוָה, עֹשָׂה חָיִל. יְמִין יְהוָה, רוֹמֵמָה; יְמִין יְהוָה, עֹשָׂה חָיִל. לֹא-אָמוּת כִּי-אֶחְיֶה; וַאֲסַפֵּר, מַעֲשֵׂי יָהּ. יַסֹּר יִסְּרַנִּי יָּהּ; וְלַמָּוֶת, לֹא נְתָנָנִי. פִּתְחוּ-לִי שַׁעֲרֵי-צֶדֶק; אָבֹא-בָם, אוֹדֶה יָהּ. זֶה-הַשַּׁעַר לַיהוָה; צַדִּיקִים, יָבֹאוּ בוֹ. אוֹדְךָ, כִּי עֲנִיתָנִי; וַתְּהִי-לִי, לִישׁוּעָה. אֶבֶן, מָאֲסוּ הַבּוֹנִים-- הָיְתָה, לְרֹאשׁ פִּנָּה. מֵאֵת יְהוָה, הָיְתָה זֹּאת; הִיא נִפְלָאת בְּעֵינֵינוּ. זֶה-הַיּוֹם, עָשָׂה יְהוָה; נָגִילָה וְנִשְׂמְחָה בוֹ.
אָנָּא יְהוָה, הוֹשִׁיעָה נָּא;
אָנָּא יְהוָה, הַצְלִיחָה נָּא.
בָּרוּךְ הַבָּא, בְּשֵׁם יְהוָה; בֵּרַכְנוּכֶם, מִבֵּית יְהוָה. אֵל, יְהוָה--וַיָּאֶר-לָנוּ: אִסְרוּ-חַג בַּעֲבֹתִים--עַד קַרְנוֹת, הַמִּזְבֵּחַ. אֵלִי אַתָּה וְאוֹדֶךָּ; אֱלֹהַי, אֲרוֹמְמֶךָּ. הוֹדוּ לַיהוָה כִּי-טוֹב: כִּי לְעוֹלָם חַסְדּוֹ.
יהללוך יהוה אלהינו כל מעשיך וחסידיך צדיקים עושי רצונך וכל עמך בית ישראל ברנה יודו ויברכו וישבחו ויפארו וירוממו ויעריצו ויקדישו וימליכו את שמך מלכנו כי לך טוב להודות ולשמך נאה לזמר כי מעולם ועד עולם אתה אל ברוך אתה יהוה מלך מהלל בתשבחות