| DNS Oversimplified
|
|
30 Sep 03 |
|
[ print
link
all
] |
I have been "surfin’ the net" since the days when Mosaic
was your browser of choice, and I have a vague understanding of how the
Internet "works". Recently I have been trying to upgrade that
vague knowledge into something a little more concrete and have been reading
up on how DNS operates. I came across this site: DNS Oversimplified and found it to be
very helpful. If you can configure your own nameserver with one hand tied
behind your back, you won’t find anything new there. But for the
casual surfer, it is a gold mine of good information.
comments
|
| Leaky Abstractions and the Criteria Library
|
|
13 Sep 03 |
|
[ print
link
all
] |
|
Joel Spolsky writes about leaky abstractions.
Abstractions leak when the underlying implementation shows through. A leaky
abstractions that have always bothered me is moving from a small collection
of objects to a database backed collection.
Here’s an example. Suppose you had a list of person objects, and you
wanted to extract everybody under the age of 21. You might write some code
like this.
# ARRAY VERSION
youngsters = people.select { |p| p.age < 21 }
This works great with small in-memeory collections. But what happens when
you start storing your objects in a database. Fetching every row from the
database and running the comparison on the is not only slow, it defeats the
purpose of using a database. So suddenly your code becomes …
# SQL VERSION
youngsters = people.select "person.age < 21"
We pass in a string (instead of a block) so we can use the string to build
a SQL query. Somewhere buried inside of the select method is a statement
that looks something like this:
def select(query_string)
sql = "SELECT * FROM person WHERE #{query_string}"
# Use the SQL string to query the database
end
We have to switch to string encoded queries because we have a leaky
abstraction.
Ryan Pavlik published an interesting Ruby library, called Criteria, that
helps to plug this particular leak. Ryan’s Criteria library provides
table objects that work like this …
require 'criteria/sql'
table = Criteria::SQLTable.new("person")
query = table.age < 21
puts query.select # => "SELECT * FROM person WHERE (person.age < 21)"
Wow. Did you see what just happened? We took an ordinary Ruby expression
(table.age < 21) and somehow captured it in data — data that we
used to generate an SQL statment. Lets skip how this works for
just a moment and consider what we can do with this.
Using Ryan’s criteria, we can now write a database backed collection
that doesn’t require us to pass in SQL fragments to do arbitrary
queries. Instead we can express our queries in natural Ruby syntax and let
the library handle the conversion.
A collection that takes advantage of Criteria might look something like
this (in part):
class People
def initialize(db)
@db = db # DBI database handle
end
def select(&block)
table = Criteria::SQLTable.new("person")
query = block.call(table)
@db.select_all(query.select).collect { |row|
Person.new(row['name'], row['age'])
}
end
end
How it Works
The mechanism behind Criteria is surprisingly simple. It parses the
expression by executing it. Sending any message to a table object will
cause it to remember that message in a special criterion object, which is
returned as the result of the message. Futher messages to the criterion
object are also recorded and new criterion objects are returned. The end
result is a network of criterion objects that resemble the parse tree for
the expression being evaluated. Once you have that parse tree, the rest is
easy.
Remaining Leaks
The Ruby code to generate the parse tree is about 50 lines of code, a real
tribute to the flexibility of Ruby. However, it is not perfect. Since the
library depends on recording messages, anything that is not a message will
be lost. Since almost everything in Ruby (including operators) send
messages, this is not a problem — except for the short circuit logic
operators && and ||. So there’s one leak, Criteria
expressions need to use & and | instead of the more natural &&
and || operators.
The second leak deals with how types are coerced in Ruby. The expression
t.age < 21 will work because the less than message is sent to
the table object and it knows how to handle it. However, the expression
21 > t.age will send the message to the integer object and it
doesn’t know how to handle tables.
Fortunately the restrictions are fairly mild. The Criteria library
represents some wonderful "outside the box" thinking to attack a
particularly difficult problem.
comments
|
| Unlimited Undo
|
|
10 Sep 03 |
|
[ print
link
all
] |
|
Charles Miller writes about
user interfaces and the trend to put up klunky dialog boxes to confirm
"dangerous" actions. The dialogs rarely stop anyone from making a
mistake. Folks will just click on them automatically without thinking about
them. I know I do.
Charles writes: "Make your actions undo-able. Make your deletions
un-deletable." Then the user always has the ability to undo any
accidents that may have slipped past his fingers.
This is a great idea. I know that when I started using Emacs as my main
editor, it changed the way I editted text. Emacs has an unlimited undo
feature, where you can undo the file all the way back to the beginning of
the edit session. Suddenly I was no longer afraid to try out new features
in the editor. Sure, it might screw up the file, but savlation was only a
few keystrokes away.
Let’s start a campaign to rid the world of those annoying
confirmation queries!
comments
|
| "Using Ruby" Now Available
|
|
31 Aug 03 |
|
[ print
link
all
] |
|
I did a presentation on using Ruby at work last week. It is meant to be a
quick intro into using Ruby, targeted mainly at Java Programmers. The talk
was constrained to one hour so there are a lot of things that got glossed
over. I expect to update the talk as time goes on, so feedback is welcome.
You can find it here.
comments
|
| Feedback on Line Noise (and other stuff)
|
|
26 Aug 03 |
|
[ print
link
all
] |
Wow, there were lots of responses to the LineNoise and PythonAndRuby postings
last week. Here’s some of the feed back.
On Line Noise
First of all, let me remind everyone that a line noise score is for
entertainment purposes only, and trying to derive something deep or serious
about a language from its line noise score is counterproductive.
Brian
Marick
reminded me of a signature survey that Ward Cunningham had done. Ward
throws out all but a few characters to generate a signature for a Java
file. Surprisingly, you can tell a great deal about a file from the
signature. Check it out here.
James
Robertson
wonders how Smalltalk would fare against the line noise filter. There is
some question how to exactly count Smalltalk code, since there is no
standard text representation for Smalltalk. I tried the fileout format from
Squeak and removed all the extra puctuation that is added during fileout.
Here is my results for Smalltalk …
animal.st (45): :#:'':'':'':'':''.:#:'':'':'':'':''.:=:.:[:|]
James came up with a score of 35, but on his own source code running on a
different version of Smalltalk. A lot of the Smalltalk score came from the
class declarations, and if VisualWorks differs from Squeak significantly in
the way classes are declared, that could easily account for the difference.
Ted Leung would like to
see a larger sample. He suspects that the Ruby line noise ratio would be
higher. Perhaps, but I haven’t seen it. From the very little research
I’ve done, Ruby and Python get roughly the same line noise score on
larger files.
On Python and Ruby Mindsets
Richard
comments on the PythonAndRuby posting
and complains about my characterization of the Python "."
operator. He says
- "x[y]" is a dictionary lookup. "x.y" is an
attribute lookup that can have nothing at all to do with a dictionary.
He mentions that the __ getattr __ and __ getattribute __ hooks in Python
may redirect the "." operator so that no actual dictionary is
used. He suggests "attribute lookup" would be more accurate.
His terminology is probably more accurate, and I was delibrately glossing
over the details. But the main point that Python programmers view the
"." as some type of lookup is still very pertinent.
The Right Attitude
In the midst of all these Python and Ruby comparisons, I would like to wrap
up with a posting from Mauricio Fernandez’s blog.
Mauricio recounts a conversation with a stranger on a plane where they
discuss design patterns and dynamic languages.
- It was only much later that I remembered that he told me he was a
Pythonist. This means (obviously) that we should have fought to death. Too
bad we forgot it ;)
I like that attitude!
Thanks to everyone for the feedback.
comments
|
| Python and Ruby
|
|
22 Aug 03 |
|
[ print
link
all
] |
|
Recently there has been a spat of Ruby VS Python threads, both in ruby-talk
and in comp.lang.python. I don’t recommend reading the threads, they
are repetive, filled with misunderstandings (from both sides) and more
personal attacks than technical content.
However, one thing I have noticed is that Rubyists and Pythonistas bring
different assumptions to the table. What is intuitive to one group seems ad
hoc and downright weird to the other.
Here’s one example that I noticed.
In Python, the construct "x.y" means lookup the name
"y" in the dictionary of "x". (Ok, that’s
simplified, but you get the picture). The result of that lookup may be a
value (instance data), or may be a function that can be called (member
functions). In other words, "." is basically a lookup operation.
In Ruby, the construct "x.y" means send a message named
"y" to the object represented by "x". All operations on
an object must be done through the sending of a message.
This leads to all kinds of non-intuitive behavior if you are expecting the
wrong thing. For example, one Pythonista commenting on the fact that
"x.y" and "x.y()" are equivalent asks the question:
"How do you get access to the y function inside x?".
A Rubyist’s response to the question is "Huh?". You see,
there are no "functions" in Ruby. At least not in the way the
question assumes. Ruby’s unit of execution is a method, and methods
are not first class objects in Ruby. If you wish to have a callable object
that references a method on a particular object, then that is easily
accomplished by asking the object for a Method object.
o = Object.new
m = o.method(:to_s)
m.class # => Method
Note the difference between a method (an internal Ruby construct) and a
Method object (an object that represents the internal method bound to an
instance). To "call" a method object (or any callable object in
Ruby), you send the object the "call" message. So, the result of
calling m should be identical to the result of sending to_s to o directly.
And we see that it is.
m.call # => "#<Object:0x401c8618>"
o.to_s # => "#<Object:0x401c8618>"
Although Python and Ruby are similar in many ways, their basis for
computation is quite different in some fundamental ways. Python follows C++
and Java in using "." as a structure selector operation, while
Ruby is more closely attuned to Smalltalk’s message sending paradigm.
Both approaches work and are interally consistent, but be prepared for
surprises if you try to interpret one using the assumptions of the other.
comments
|
| The Simplest Unit Test Framework That Could Possibly Work
|
|
22 Aug 03 |
|
[ print
link
all
] |
|
I was helping a friend in C++ the other day. While I religiously use a test
framework in my day to day work (e.g. JUnit for Java or Test::Unit for
Ruby), I haven’t used C++ for over three years and didn’t have
C++ Unit handy.
Then I remembered the C/C++ assert macro. We wrote a a series of small
functions that looked something like this:
void TestCanRun () {
assert( CanRun( ... ) );
assert( ! CanRun( ... ) );
// and so on ...
}
I made a TestAll() function that called all my little test functions and
arranged to have TestAll() called as the first thing in main(). If all the
assertions worked, the program completed normally. If an assertion failed,
the program would abort with an informative error message.
It worked great. I wouldn’t leave production code like that, but it
was a great way to introduce Test Driven Design to someone who had not seen
it before, without getting into all the nitty-gritty details of setting up
a full blown test suite.
I guess my motto is that friends shouldn’t let friends program
without tests.
comments
|
| Line Noise
|
|
22 Aug 03 |
|
[ print
link
all
] |
|
There is a thread
on the ruby-talk list on punctuation as noise. Hal Fulton wrote a short
program to analyze the symbol to puncuation ratio of a program and then
compared the results on several programs in different languages.
I did something similar a while back. A Java programmer had taken a look at
Ruby and declare that he didn’t like all that "line noise"
in the language. He was refering to the "@" and "$"
characters used to mark instance variables and globals. I pointed out that
Ruby actually uses quite a bit less punctuation than Java, and wrote the
following linenoise program do demonstrate.
#!/usr/bin/env ruby
ARGV.each { |fn|
noise = open(fn) { |file| file.read }.gsub(/[A-Za-z0-9_ \t\n]/m, "")
puts "#{fn} (#{noise.size}): #{noise}"
}
Linenoise will strip out all alphanumeric characters and white space,
leaving only the "line noise" behind. Running linenoise on a
series of small programs written in different languages produces this
(edited slightly for line breaks) …
animal.cc (83): #<>{:()=;};:{:();};::(){::<<"\";}:{:();};::(){::
<<"\";}(){*[]={,};(=;<;++)[]->();;}
Animal.java (67): {{();}{(){..("");}}{(){..("");}}([]){[]=[]{(),()}
;(=;<.;++)[].();}}
animal.pl (41): ;{{};}{"\";};{{};}{"\";};$(->,->){$->();}
animal.py (23): :():"":():""[(),()]:.()
animal.rb (10): """"[.,.].
The number in the paranthesis is the number of line noise characters in the
file.
What I find interesting is the amount of semantic information that still
comes through the "line noise". For example, the
"#<>" sequence in the C++ code is obviously an include
statement for something in the standard library and the
"<<" are output statements using "cout".
It would be interesting to see if you could determine the language given
only the line noise. You could tell Java from C++ by the ";}" vs
";};" punctuation. Python is pretty clear from the
’:():"":():’ style patterns.
Before I go, here is the source code to the Animal programs I used in my
examples…
Language: C++
#include <iostream>
class Animal {
public:
virtual void talk() = 0;
};
class Dog : public Animal {
public:
virtual void talk();
};
void Dog::talk() {
std::cout << "WOOF\n";
}
class Cat : public Animal {
public:
virtual void talk();
};
void Cat::talk() {
std::cout << "MEOW\n";
}
int main() {
Animal * (a[]) = { new Dog, new Cat };
for (int i=0; i<2; i++)
a[i]->talk();
return 0;
}
Language: Java
public class Animal {
interface IAnimal {
void talk();
}
static class Dog implements IAnimal {
public void talk () {
System.out.println("WOOF");
}
}
static class Cat implements IAnimal {
public void talk() {
System.out.println ("MEOW");
}
}
public static void main (String args[]) {
IAnimal[] zoo = new IAnimal[] { new Dog(), new Cat() };
for (int i=0; i<zoo.length; i++)
zoo[i].talk();
}
}
Language: Perl
package Dog;
sub new {
bless {};
}
sub talk {
print "WOOF\n";
}
package Cat;
sub new {
bless {};
}
sub talk {
print "MEOW\n";
}
package main;
for $a (Dog->new, Cat->new) {
$a->talk();
}
Language: Python
class Dog:
def talk(self):
print "WOOF"
class Cat:
def talk(self):
print "MEOW"
for a in [Dog(), Cat()]:
a.talk()
Language: Ruby
class Dog
def talk
puts "WOOF"
end
end
class Cat
def talk
puts "MEOW"
end
end
for a in [Dog.new, Cat.new]
a.talk
end
comments
|
| Catch Curly Curly
|
|
19 Aug 03 |
|
[ print
link
all
] |
|
Java’s checked exceptions are the type of thing that sound good at
first glance, but have seriousl drawbacks in productions use. I was
pleasantly surprised when Bruce Eckel echoed many of my observations in
his checked exceptions essay.
More recently, Anders Hejlsberg (the designer of the C# language) has weighed in with his
opinions of Java’s checked exceptions.
Here are some quotes from the article
- The throws clause […] requires you to either catch declared
exceptions or put them in your own throws clause. To work around this
requirement, people […] decorate every method with, "throws
Exception." That just completely defeats the feature, and you just
made the programmer write more gobbledy gunk. […]
Yep, that’s me. I use throws Exception a lot.
Anders goes on to comment that the focus is not generally on
handling exceptions, but making your code robust in the presence
of thrown exceptions. And that generally means finally blocks. He
observers that …
- In a well-written application there’s a ratio of ten to one, in
my opinion, of try finally to try catch.
Interesting. I wonder how much duplication occurs in all those finally
blocks. That’s why I like Ruby’s ability to abstract the
finally (ensure in Ruby) to one location.
Finally, here’s where I got the title for this blog entry.
- I can’t tell you how many times I’ve seen this — they
say, "try, da da da da da, catch curly curly." They
think, "Oh I’ll come back and deal with these empty catch
clauses later," and then of course they never do.
I"m not guilty of the catch curly curly syndrome, but I’m not
surprised that its a common problem. Bruce Eckel says it best when he calls
Java’s checked exceptions a failed experiment.
comments
|
| You Say Potato, I Say Potahto
|
|
14 Aug 03 |
|
[ print
link
all
] |
I came across a reference to the elastiC language in a blog
entry by Hans Nowak. Hans starts out his blog entry with this …
- elastiC. Quite an interesting language; Pythonic in many ways. At first
glance, it seems to combine some good stuff from Python, Perl, Smalltalk,
Lisp and C. …
Ok, sounds interesting. I always enjoy new languages. So I clicked over for
a quick look at the elastiC site. With the exception of C-Like syntax and
small footprint, the list of languages features were very reminiscent of
Ruby. I did a quick scan of the language docs and it reminded me a lot of
Objective-C, but I didn’t see anything to hold my interest.
As I return to Hans’ blog entry, I noticed his next sentence …
- … Yet (again at first glance) it doesn’t seem as kludgy as
Ruby.
What!? Ruby? Kludgy? Certainly not!
And yet, Hans seems to think so. Perhaps Hans has given Ruby the same quick
once over that I just gave elastiC and didn’t see the unifying
principles behind Ruby. If so, I can forgive him the slight.
And then again, maybe Hans just has different tastes in programming
languages. After all, my daughter professes that GoldStar Chili is the best in the
world, while I maintain that Skyline Chili has no peer —
and we are able to live in the same house.
It sounds like Hans is a Gold Star type of guy.
That’s OK, because I going to continue eating at Skyline.
comments
|
|
|