Tuesday, July 13, 2004

Mixins Paper

Modular Object-Oriented Programming with Units and Mixins explains why mixins are so important. Why aren't more people turned on to this stuff?

Friday, July 09, 2004

The Point of JDNC, XAML, and XUL

My colleague assumed that the point was to enable round-tripping gui builders. He didn't even think that web deployment was a significant goal.

Now that he mentions, I can't see that html/xml confers any technical benefit for running in the browser. What about round-trip editting? Is round tripping gui building really so much easier with an xml dialect than with code which uses simplified widget api?

Monday, July 05, 2004

Better AOP

Here's a better way to support Aspect Oriented Programming:
  • mixins!
  • wildcard methods
  • methods that override constructors (and superclasses) for a class

This is approach is nice because it doesn't introduce an entirely new abstraction for extending code. With mixins, the caller can extend a class that the mixin author didn't know about. With wildcard methods, the mixin can be more flexible.

Overriding constructors is probably the most controversial part, by which I mean lanugage support for the pattern of my July 1 entry. For example, many component libraries consist of several classes, but for ease-of-use, only make a few of those classes available. In order to extend the behavior of the implementation classes, it would be nice to be able to do something like:

public class A2 extends A {

B.new() {
return new B2();
}
}

Friday, July 02, 2004

Nature of Aspect Oriented Programming

If you've been frustrated about exactly what AOP is, you'll enjoy Aspect-Oriented Programming is Quantification and Obliviousness. Also interesting is the realization that Aspect-Oriented Programming is just "intercessional reflection"

Thursday, July 01, 2004

More on Objected Oriented Design

In order for a class to be extensible, it must never obtain references to objects directly.

It must use separate factory methods whose sole responsibility is to obtain and return those references. That way, a subclass can easily override those methods to substitute different objects.

For example, instead of:

public class Fu {

public void doSomethingWithBar() {
new Bar().doSomething();
}
}

use the following:
public class Fu {

public void doSomethingWithABar() {
newBar().doSomething();
}

protected Bar newBar() {
return new Bar();
}
}

which allows:
public class Fu2 extends Fu {

protected Bar newBar() {
return new Bar2();
}
}

A Solution to the Fragile Base Class Problem explains (peripherally) why these methods should be protected.

This is the GOF factory pattern, but it should be applied more generally. Possibly people restrict its use because they're worried about components not being sufficiently well thought out. There may be some subtle dependency on the actual object implementations. To specify that there isn't any such dependency, it would be be better to use an interface for the return type of these factory methods:

protected BarInterface newBar() {

return new BarImplementation();
}

Wednesday, June 30, 2004

More on Minyan Sign-in

Here's a python implementation of the email part of my minyan counter design. Beware, it hasn't been tested.

#!/usr/bin/python

PREVIOUS_COUNT_FILE = '/tmp/m-last'
YIDDEN_FILE = '/home/josh/minyan_goers'
INTERVAL = 60
LATE_INTERVAL = 5
LATE_TIME = '14:00'
CUTOFF_TIME = '15:10'
PROXY = {'http':'http://aproxyserver:82'}
TALLY_URL = 'http://aminyancounter.cgi'
FORM = '<form action="'+TALLY_URL+'" method="post"><input type="hidden" name=email value="$EMAIL" /><input type="submit" value="yes" /><input type="submit" value="no" /></form>'

from os.path import getmtime
from datetime import date, datetime, timedelta
from time import time, mktime, strptime, sleep
from sets import Set
from smtplib import SMTP
from email.MIMEBase import MIMEBase
from email.MIMEMultipart import MIMEMultipart
from dstring import dstring, safedict
from re import compile, findall
from urllib import urlopen

def my_print(*s):
print datetime.now().strftime('%H:%M:%S'), ' '.join([str(x) for x in s])

def parse_time(s):
struct = list(strptime(s, '%H:%M'))
struct[0]=1970 #can't have negative seconds since the epoch
return datetime.fromtimestamp(mktime(struct))

class MinyanMessage(MIMEMultipart, object):
def __init__(self, yidden):
super(MinyanMessage, self).__init__('alternative')
self.yidden = Set(yidden)
self.plain = MIMEBase('text', 'plain')
self.html = MIMEBase('text', 'html')
self.attach(self.plain)
self.attach(self.html)
self['From'] = 'me'
self['To'] = self['Subject'] = '' #set for each recpient

def lost_it(self, accepters=(), decliners=(), last_call=False):
self.recipients = self.yidden - Set(decliners)
self.text=['we need $NEED_COUNT for a minyan since someone cancelled', FORM]
if last_call:
self.last_call_adjust_text()
self.send()

def got_it(self, accepters=(), decliners=()):
self.recipients = accepters
self.text=['we have a minyan!', '']
self.send()

def not_got_it_yet(self, accepters=(), decliners=(), last_call=False):
self.recipients = self.yidden - Set(accepters) - Set(decliners)
self.text=['we need $NEED_COUNT for a minayn', FORM]
if last_call:
self.last_call_adjust_text()
self.send()

def cancelled(self, accepters=(), decliners=()):
self.recipients = Set(accepters)
self.text=['there will be NO minyan today', 'Only $HAVE_COUNT people signed up.']
self.send()

def last_call_adjust_text(self):
if True:
self.text[0]='LAST CALL! '+self.text[0]
self.text[1]='If people don\'t sign up by $CUTOFF_TIME, there won\'t be a minyan today.'

def send(self):
s = SMTP()
expand = safedict({'HAVE_COUNT':self.count, 'NEED_COUNT':self.count < 10 and 10-self.count or 0, 'CUTOFF_TIME':CUTOFF_TIME, 'TALLY_URL':TALLY_URL})
s.connect()
for recipient in (self.recipients):
my_print('Mailing', recipient)
expand['EMAIL']=recipient
self.replace_header('To', recipient)
self.replace_header('Subject', dstring(self.text[0]) % expand)
self.plain.set_payload(dstring(self.text[1]) % expand)
self.html.set_payload(dstring(self.text[1]) % expand)
s.sendmail(self['From'], recipient, self.as_string())
s.close()

re = compile(r'[\w\.\-+=!%]+?@[\w\.\-+=!%]+')
late_time = parse_time(LATE_TIME).time()
cutoff_time = parse_time(CUTOFF_TIME).time()
yidden_file = open(YIDDEN_FILE)
message = MinyanMessage([line[:-1] for line in yidden_file])
while True:
decliners = ()
page = urlopen(TALLY_URL, proxies=PROXY)
page_contents = ''.join(page.read())
accepters = re.findall(page_contents)
message.count = count = len(accepters)

if count == 0:
my_print('No one has signed up for minyan yet.')
continue

previous_count = 0
try:
if date.fromtimestamp(getmtime(PREVIOUS_COUNT_FILE)) == date.fromtimestamp(time()):
previous_count_file = open(PREVIOUS_COUNT_FILE)
previous_count = int(previous_count_file.read())
except:
pass
previous_count_file = open(PREVIOUS_COUNT_FILE, 'w')
previous_count_file.write(str(count))
previous_count_file.close()

now = datetime.now()
if now.time() > cutoff_time:
if count < 10:
message.cancelled(accepters)
elif previous_count < 10:
message.got_it(accepters)
break

interval_to_sleep = now.time() < late_time and INTERVAL or LATE_INTERVAL
if (now+timedelta(minutes=interval_to_sleep)).time() > cutoff_time:
is_last_call=True
else:
is_last_call=False

if previous_count >= 10 and count < 10:
message.lost_it(accepters, decliners, last_call=is_last_call)
elif previous_count < 10 and count >= 10:
message.got_it(accepters, decliners)
elif previous_count < 10 and count < 10:
message.not_got_it_yet(accepters, decliners, last_call=is_last_call)

my_print('Sleeping for', interval_to_sleep, 'minutes...')
sleep(interval_to_sleep*60)

Python Warts

  • Constructors in default arguments are only evaluated once
  • Nobody uses super.__init__() properly
  • It's weird to have a single immutable collection type (tuple)
  • It's confusing having both re.match and re.search
  • iterable strings cause hard to find bugs, when list(mystring) would be sufficient
  • Building a single element tuple requires a trailing comma
  • Exceptions should go in a namespace
  • There's no true ternary operator
  • The super() call requires specifying the class (and self)
  • Having both staticmethods and classmethods is confusing
  • There's currently no way to limit extreme dynamic behavior
  • Regular functions should obviate "unbound methods"
  • "lamba" and "def" use different syntaxes
  • Iterators aren't used enough
  • Comparing different types returns false instead of raising an exception
  • print should be a builtin function, not a statement
  • Overriding all operators is not yet supported, would allow LINQ alike

Many of these are going to be changed as described in python3000 pep.

Tuesday, June 29, 2004

More on Presentation Done Right

Here's a great paper about it: Enforcing Strict Model-View Separation in Template Engines describing The StringTemplate Template Engine

The author supposes that there should be a different language for template logic, while I supposed that one language and a library would be sufficient. According to my thinking, "templates" only require a way to name each presentation token, and that can be an xml "id" attribute or some other attribute. My thinking doesn't address keeping presentation logic out of business logic, but it certainly keeps business logic out of the actual presentation.

The StringTemplate approach doesn't seem to care about WYSIWYG tools, though with one exception, it should work fine with pointy brackets as expression delimiters. The one exception is "<multi-valued-attribute:{anonymous-template}>" which would be better as "<for multi-valued-attribute>anonymous-template<endfor>".

For both approaches, I envision either mock objects that can populate templates to yield example pages, or actual pages pulled from a live system. The framework should allow a WYSIWYG tool to edit these populated templates, and then be able to apply the changes to the underlying raw templates. The difficult part is figuring out how to apply changes to included or inheritted templates. For example, if A ineherits from B, and someone changes a view of A using the framework, should the change be applied to A or to B?

Wednesday, June 23, 2004

Object Oriented Design

Design Principles and Design Patterns is a good paper. This entry summarizes the "Principles" part of that paper.

Object oriented programming is all about using old code to do new things. It should be easy to add new functionality without breaking the old. It should also be easy for other projects to reuse code without a lot of extra effort.

This a problem of dependency management, which may be solved using encapsulation. Encapsulation enables specifying and reducing dependencies between objects.

If code is properly modularized, its behavior can be changed by extending it instead of modifying it. (This is referred to as the "Open Closed Principle".) If code isn't modified, then code doesn't break.

When does it make sense to implement a particular abstraction, or extend a particular class? The new class should be usable anywhere the abstraction (or parent class) occurs. This is the "Liskov Substitution Principle". You might think, for example, that a Square would be a natural subclass of a Rectange. However, this potentially violates Liskov. Since the Square is more contrained than the Rectange, it can't necessarily be used in every place that a Rectangle is used. (Presumably, in java you could make it right by declaring that the superclass setLength() method may throw ReadOnlyWriteAttemptException.)

It's impossible to structure code in such a way that extension can accomplish any possible change, so the designer must anticipate the most desirable changes. It helps to keep abstractions as small as possible.

Monday, June 21, 2004

Semweb and Presentation

Some leaders of the movement think that bootstrapping is the greatest remaining obstacle to the semweb. If that's true, then why is everyone neglecting the greatest single obstacle to the bootstrapping process... ?

Why isn't there any easy-to-use framework for presenting RDF on the web? Shouldn't the semweb be able to achieve at least some of the actual results of the existing web? Shouldn't it support all the comfortable old web data, in addition to the annotations, agents, and allegories?

Here's an example: I maintain a site that lists the synagogues in my neighborhood. It has a page for each synagogue and it has schedules of events. Many synagogues have similar pages. Now these web pages are going to be the most popular representations of the data for a long time to come, but I'd jump at the chance to run the site using RDF.

All I need is a simple way to maintain my simple, static web presentation. Even before there's a standard synagogue ontology, and before I can write code to graph the times that congregations across the country begin to pray every morning, I'd use RDF. Maybe that'd even give me an easy way to list the schedule for the whole neighborhood on one page, and for each individual synagogue on its own page.

I don't want XSL, and I don't want a full-blown content management system. I want a simple way to start using RDF to back my web sites.

Minimal Semantic Web

The Semantic Web project is an effort to remove arbitrary and wasteful barriers between applications. We make it possible for them to share some structure, and have partial understanding of data created elsewhere.

Wednesday, June 16, 2004

Java Constructors Suck

  • Subclasses' constructors can't catch or override exceptions thrown by Superclasses' constructors
  • Subclasses must explicitly implement each constructor of their Superclasses, even if they don't want to override
  • Interfaces can't specify constructors
  • Reflection doesn't expose constructor parameter names, and there is no language support for describing correspondence between constructor parameters and getter/setter attributes

Tuesday, June 15, 2004

Fedora Core 2

Beautiful. So beautiful that my list of complaints is short:
  • gnome keyboard switcher applet doesn't work
  • up2date is broken (though command line "yum" works well)
  • the "Run Application" dialog can't be dismissed by pressing the escape key
  • still bundles mozilla instead of firefox
  • laptop power management isn't ready for prime time
  • no ntfs support out of the box
  • poor multimedia out of the box
  • volume buttons and trackpad scroller don't work
The first three only happenned on my laptop, but worked fine on my desktop.

By beautiful, I mean fast, smooth, and consistent, with important applications working out of the box (mozilla, evolution, and openoffice). And it has lovely hebrew fonts.

Monday, June 07, 2004

Minyan Sign-in

There've probably already been a bunch of implementations of minyan sign-in web pages. They're useful when you don't always get a minyan, and you don't want to have nine people show up and wait for no reason. They're less useful because no one actually wants to sign-in.

A couple of features I understood from the beginning: it should be as a easy as possible to sign-in (with cookies for example), and people should be able to withdraw their commitments.

I just thought of a major improvement:

  • sign-in ids should be email addresses
  • after a specified time, addresses that have not signed-in should get periodically reminded via email
  • whenever a minyan is achieved or lost, addresses that have signed-in should get emailed
So there are two incentives to sign in:
  • you get notified about whether there'll be a minyan
  • you don't get annoying reminder emails
Would only one of these incentives be sufficient? Neither is excessively onerous because:
  • if you really hate email reminders, you can just go to the site and mark yourself "can't make it" even if maybe you can.
  • if you want to the know the status of the minyan, you can view it directly on the site

Changes to minyan status probably shouldn't be mailed out immediately (at least earlier in the day) because the status could naturally change a lot on its own. For example, if ten people sign in at 10am and one drops out at 10:15, you have a reasonable chance of getting a replacement by 11.

In order to make it easiest to sign in, the form should also be inlined in the reminder emails.

Tuesday, May 25, 2004

Horrible SQL

Get the aggregate of one column, but only if another column has a null


select !count(stopped) as continuously, min(started) from someTable where ownerId = ? having count(*) <> count(stopped)

Sort reverse chronologically, but with nulls at the beginning


select * from someTable order by if(dateField,dateField,"9999-12-31") desc

Delicious

http://del.icio.us/ is incredibly cool.

Thursday, May 20, 2004

Presentation Done Right

WYSIWYG is the right way to do presentation (visual rendering of data). This stuff does not belong in code. That a given widget is five feet tall and bright pink does not belong in code. It does not belong in a specially formatted template. It doesn't even belong in a property sheet. It belongs in a directly presentable document, which is preferably maintained with a WYSIWYG tool. Once this tool exists, there is zero advantage to doing it any other way.

This is a solution for at least three classes of presentation: web pages, print, and desktop applications. This is the major innovation of XUL and XAML. The gui builder doesn't generate code directly, but generates a document which contains only presentation information.

The right way to do it is with Push MVC. Push MVC means that the legal static presentation documents are manipulated by external code. Pull MVC means code is embedded in pseudo-documents, and it's a broken concept. Even if the WYSIWYG tool can be prevented from choking on or breaking the embedded code, it obscures the presentation, and it potentially confuses the people who work on the presentation.

The code itself should generate template documents for each presentation element, framed by example presentation to show what the individual elements will look like in context. For HTML at least, the template links could be generated to work properly within the template.

It would nice if there were a simple language for specifying bindings between presentation documents and data documents. For simple cases, actual code need not be necessary at all. (For complex cases, not only is code necessary, but an actual presentation api is also needed. For example, you may want a widget to change colors based on system status.)

Except for desktop applications, we're already close to having a very sweet WYSIWYG tool. Openoffice has a beautiful format which largely uses standard sub-formats for presentation (SVG, XSL:FO). The problem is that it can't yet export to a purely standard format document. The advantage of the purely standard format document, of course, is that it can be transformed to html or pdf without launching the full openoffice application.

XMLC appears to be the only implementation of these ideas so far. Zope page templates don't seem to have it right; they have a lot of cruft in the presentation document. If nothing else, this confuses people about the role of that document.

Monday, May 10, 2004

Old friends and binary subscriptions

Blogs should be a great medium for keeping up with old friends. For those friends that you don't have time to correspond with, your blog gives a nice window into what you're up to.

It would be nice if subscriptions weren't so black-and-white. Currently subscribing to a blog is a relatively large commitment. If I find it boring, I don't want to have to always explicitly dismiss its unread entries. Blog viewers should sort in order of interest, and automatically expire old entries which are not ranked sufficiently interesting.

This is an especially good idea because it creates a practical incentive for ratings.

Friday, May 07, 2004

Gubbish

Danny Ayers is probably referring to "gubbled", from Martian Time-Slip. I wonder if blogspot or weblogs.com automatically sends something like a trackback.

Thursday, May 06, 2004

Another cute feature

Your email client's "attach" button would automatically upload to the web, and insert a link to the uploaded file (instead of actually attaching it).

The web upload area, username, password, etc. should only need to be configured once, just like other account settings. Conceivably, the server for that upload area could support webdav or deltav, to enable your recipients to make revisions to the "attachment" that you sent them.

I had this idea last year, though Jon Udell wrote about it recently.