Redux
The job search: It’s hard to tell, but I think it went well. When a blog post rhymes, I eat limes. Friday is here, and I’m drinking a beer! Let’s end this.
California was quite nice in the daytime. I flew the redeye back this morning and I’ve been napping all day as a result. JetBlue may have a lot of forward leg room, but from side-to-side it’s torture. I did meet a couple of cool people, though. To my right, this guy. To my left, director of development here. I enjoy a little schmoozing now and then.
Blog news: Upgraded to Typo 4.1. Pain in the butt. Broke some CSS. Backend seems nicer. We’ll see.
Other: I did manage to get a little bit of work done. Expect a new release of Handshake soon with some small updates (documentation, checked_self, no blame system yet).
Well, I'm here
So I’m out in California tonight and tomorrow for An Interview. I’ve never been any farther west than Florida before, and so far… well, it looks a lot like Boston’s Rte. 128 belt, to be honest. I used to read way too much Wired Magazine in high school and I remember, if you can believe this, dreaming about Silicon Valley and the brave new pioneers out there forging crazy new Internet services. So even if nothing comes of The Interview, I’m tickled just to be able to visit.
Unfortunately I probably won’t be able to get out much. I’ve always wanted to see San Francisco but there’s not really enough time to make it from Mountain View (cough) to there and thence to San Jose in time to catch the red-eye back to Boston. I’ll be flying in just in time for Friday classes, where by ‘just in time’ I mean ‘six hours early’.
I had hoped to get some studying done tonight but I’m pretty beat. Apparently there was a screwup with the reservation. I arrived at the checkin desk for JetBlue only to find that, although they had me down for a ticket for San Jose to Boston, there was no such reservation for the other direction. After twenty increasingly frantic minutes on the phone with the travel agent they managed to scrounge up a seat for me, with only minutes to spare before the flight had to leave. Naturally the flight was subsequently delayed for an hour. Har har.
Study, you say? Yes, I brought textbooks in my suitcase. I’m That Guy.
Will people pay $.30 extra for high-quality DRM-free music?
Of course they will, and The Economist’s skepticism baffles me:
For years, I have been hearing avid downloaders claim that people are basically willing to pay for music but aren’t because the music industry is screwing it up—overcharging for their product, and crippling it with DRM. This is true at some level; I would snap up a Porsche at £1.20, but that is not an indictment of the firm for refusing to sell one to me at that price.
Ever since I have had enough disposable income to purchase music, rather than download it, I have made a point of doing so. This means that I had converted from a downloader to a buyer before I entered college in 2002. The iTunes Music Store is all about impulse and immediacy to me, and I’ve (occasionally) unwisely taken advantage of it, knowing full well the dangers of lock-in. For me, the promise of higher-quality, DRM-free songs at a mere 30% markup is a no-brainer.
I suppose this means that I’m not what they would describe as an “avid downloader.” But I would go one step further and claim that, for those who are, digital music has always been chiefly about convenience. I predict that the existence of a legal DRM-free method for obtaining the same, weighed against the threat of arbitrary and capricious lawsuits, will be enough to tip the scales.
As for the analogy: a Porsche is not a song; they do not cost the same to manufacture, they are not manufactured in the same way, they are not sold in the same way, and they are not governed by the same laws. The price given (£1.20) does not bear any resemblance to the cost to produce a Porsche.
Let’s turn the premise on its head. If it cost $50,000 to produce a Porsche, and the company charged $200,000 for the same car, and that car came with an arbitrary restriction (can’t go any faster than 100 MPH, say), and then no one bought the car, you would rightly point out that the company is overcharging and underdelivering. The onus falls upon the company to readjust their business model, and their choice to (for example) sue the living daylights out of their customers rather than re-examine their assumptions about the market is certainly worth an indictment or two.
But if the grand experiment fails, and people continue to download music for free despite being offered the chance to compensate artists for it, I don’t expect I’ll shed many tears for those involved. The music market is oversaturated with would-be stars. The “recording artists” most heavily promoted by the industry are the most God-awful. There is little to admire in the industry’s predatory business practices. For truly dedicated musicians, micropatronage is now widespread, and live music venues certainly aren’t going anywhere. The real victim here is copyright law, which Congress and the content holders have abused recklessly in their desperate attempts to maintain the status quo in an industry in grave need of change. Luckily, free-market alternatives exist.
Comment paradise lost
In the process of cleaning out spam I accidentally removed a bunch of valid comments. (Bad UI, Typo! Stupid human, Brian!) Sigh. I dug through the default sqlite database for this thing and it looks like the comments were legitimately removed, not just marked for deletion. The database isn’t backed up anywhere. Clearly I am a super genius. If anyone has any bright ideas, let me know in the comments, which I will try not to subsequently delete.
Maybe Wordpress is everything it’s cracked up to be. Typo has been a bit of a disappointment. It crashes occasionally too, and I’ve glanced at the logs but I haven’t nailed down the reason yet.
Way over yonder in the minor key
Purely for friends currently bored or easily distracted, here is me banging away on the guitar. The song is off of Mermaid Avenue, which I just bought and is excellent. Caveat: I haven’t slept in quite some time.
Like pouring cold water on A List Apart
A List Apart, these days a shadow of its former self, writes this week about how to ruin the user experience. Apparently the best way to ruin the user experience is to, and here I get confused, require that users enable Javascript in their browser. According to ALA founder Aaron Gustafson,
The problem here is not that Lala is using JavaScript, but that they are requiring it. The reason? Well, they apparently like the idea of loading all of the content into their pages using Ajax. In their rush to cram all that Web 2.0 stuff under the hood, they’ve alienated a good portion of Web 1.0 users and a sizable chunk of the mobile market. And they’re not alone.
This bothers me on a number of levels. First and foremost, I’m skeptical of his claim that requiring that users have Javascript enabled is “like pouring cold water in a customer’s lap.” Even if there weren’t a hundred easier ways to pour water both colder and, um, wetter, startups geared towards the youth market don’t generally worry too much about the “good portion of Web 1.0 users” (what portion exactly?) that don’t have Javascript enabled. And who in their right mind browses the internet via cell phone?
I don’t disagree that relying on the availability of AJAX, or even overusing it, is very frequently a bad idea. But I don’t think it’s anything like as bad an idea as he claims it is, and it’s a poor illustration of his points. For crying out loud, it’s 2007. They chose to optimize their website for a slick, rich user experience, and it strikes me as almost bizarre that they should be singled out for that choice.
People make choices about their market all the time. I design to separate content from style whenever possible, and I generally keep my designs simple. But I’m willing to bet that brianguthrie.com (not the blog) doesn’t render properly in IE6. I own a Mac now, and I have no easy way to test for IE6 compatibility. But it doesn’t bother me a bit. Why? Because the sooner that legacy users get the idea that IE6 is broken the better.
This is a luxury and a risk that would be unacceptable on a commercial site. But my personal site exists primarily, at the moment, to connect me to my friends and to help my career. All of my friends use modern browsers (right guys?), and I’m frankly skeptical about taking a job with anyone who browses with IE6 on a daily basis. Call me crazy, but also call me fairly sure you can’t win my $5000.
I bet you didn’t see that coming.
Handshake released as a gem
I’ve released Handshake as a live RubyGem. After you update your gem index you should be able to get it with gem install handshake, and you can browse the RDoc at handshake.rubyforge.org. A couple of things have changed since my previous blog post, so pay attention:
- Pre- and post-conditions now expect you to check assertions rather than return a boolean value from the block. I changed this because the project I’m working on requires some moderately complex rule-checking and trying to string together &&’s got really ponderous. This is probably an indication that there are better conceptualizations of that particular problem. I do quite dislike invariant and before/after employing different usage patterns.
- I’ve added a few new clauses. Check the RDoc if you need more information on these.
- I’ve fixed a lot of bugs, specifically a couple dealing with class type checking and contract inheritance.
- Contract violation error messages have been improved. The stack traces are also slightly better but not yet up to par. To generate a proper stack trace it has to be seen as originating from the method in question rather than somewhere within the bowels of Handshake, and that’s been improved but in the worst case is still two or three levels removed from the caller.
I’ll push out another release once I have a better handle on error messages. I’m having a difficult time finding an authoritative answer to what the “best” way is of getting detailed stacktrace information. Is Binding.of_caller still broken? Will debug.rb get me what I need? Or call_stack?
ruby-contract’s default behavior is to ignore contract checking unless your application’s $DEBUG flag is set. Although I haven’t made that the default behavior in Handshake, it makes a lot of sense; a contract system would probably impose inexcusable performance penalties without some way of turning it off. And if we’re not worried about performance in the first place it seems to me like a pure-Ruby implementation of something like one of the above is preferable.
As an addendum, I should note that hoe is totally pimpin’. Hyuck hyuck.
Inanity in the presence of kittens
xkcd, the best webcomic in the world, reveals its insightful theory about cat proximity. We must be vigilant.
Kitten break
And yet all I can think about is what would happen if it scratched up the screen.
As seen on Lifehacker.
Handshake vs. ruby-contract
Florian Groß (cool!) asks:
Interesting. Any chance of getting a comparison to ruby-contract? :)
ruby-contract is quite nice, and if I’d realized just how nice beforehand I probably wouldn’t have bothered with Handshake; imagine my chagrin. Its wiki is defunct, and I couldn’t gem install ruby-contract, so I figured the project was dead or unmaintained and never took a look. Lucky for me, there are enough big differences between the two in interface and approach that it’s worth spending some time to examine them. I can’t claim to understand everything that’s going on it it yet, so Florian, please correct me if I get it wrong.
Update: I misread one of Florian’s signature examples. Fixed.
Big Picture
See my previous post for the big picture on Handshake. The idea is that you define contracts directly in classes, whose behaviors are applied to subclasses. In contrast, ruby-contract’s contracts reside in their own class and are applied to other classes through the fulfills method. The code below comes from ruby-contract’s test cases (tc_contract.rb) and gives a good feel for how you would interact with the code to define contracts.
class EnumerableContract < Contract
provides :each
end
module List
module NonEmpty; end
end
class ListContract < Contract
implies List
def self.check_count() 3 end
provides :each
provides :[]
provides :size do
assert(@object.size >= 0)
end
end
class NonEmptyListContract < ListContract
implies List::NonEmpty
provides :size do
assert(@object.size > 0)
end
end
class EmptyList
def each() end
def [](idx) end
def size() 0 end
fulfills ListContract
end
class NonEmptyList < EmptyList
def size() 1 end
fulfills NonEmptyListContract
endThis code does a fair number of things that Handshake doesn’t attempt. Because Handshake agreements are defined either directly within a class or within its superclass there’s no way or reason to verify that a particular class provides or doesn’t provide a method. I also really, really like the syntax for the provides precondition checks. On the other hand, ruby-contract doesn’t appear to allow you to check class invariants, or to check postconditions in the context of all of a method’s arguments. Florian, please correct me if I’m mistaken.
Method Signature Checking
Both libraries offer method signature checking. I happen to prefer my syntax, but it’s mostly an aesthetics thing. I’m dismayed to discover that my idea of using the === operator for these clauses isn’t half as clever as I thought it was, or perhaps we are simply both clever. :)
Handshake:# accepts a string, return value doesn't matter
contract :name=, String => anything
# accepts a block, returns an array
contract :each, Block => Array
# Matches something that is an Enumerable and that responds to
# either :to_ary or :to_a.
contract :x, all?(
Enumerable,
any?(
responds_to?(:to_a),
responds_to?(:to_ary) ) ) => anything# accepts a string, return value doesn't matter
signature(:name=, String)
# accepts a block, returns an array
signature(:each, :block => true, :result => Array)
# Matches something that is an Enumerable and that responds to
# either :to_ary or :to_a.
# From ruby-contract docs.
signature :x, Contract::Check::All[
Enumerable,
Contract::Check::Any[
Contract::Check::Quack[:to_a],
Contract::Check::Quack[:to_ary]
]
]We both provided remarkably similar combinators for signature clauses, although mine are defined methods and his are classes. Mine are quite expressive in terms of the error messages they generate, but I haven’t checked ruby-contract in that regard.
Implementation
Handshake is implemented as a proxy object that resides on top of an object and intercepts all method calls, running them through invariant, signature, and general pre- and post-condition checks. It uses this to ascertain blame as necessary, although the exception messages are generated by the checks themselves. The proxy object looks, acts, and feels like the real object.
In contrast, if I understand things correctly, ruby-contract uses eval to redefine methods that the programmer wants to check. For end users, the difference lies in the way calls to methods within the same class are evaluated. In Handshake, the proxy object establishes a barrier but cannot monitor anything that occurs inside that barrier. This means that calls to private methods are effectively uncheckable. The upside is that it allows you to define invariants in terms of methods as well as instance variables without getting stuck in an infinite loop. This is particularly useful for cases where you want to extend the behavior of a core Ruby class but don’t have access to its internals; see the NonEmptyArray example from my last post.
ruby-contract also hooks into Test::Unit to allow access to its assert methods within provides blocks and utilizes its inheritance properties to automatically check those clauses as soon as a class is defined as fulfilling a contract. This is an insanely good idea and I may stealborrow some portion of it.
Conclusion
Both libraries provide some unique and complementary contract mechanisms. ruby-contract is more mature, judging by the code, although it hasn’t been updated for a while. Not to be all wishy-washy, but it looks like there are projects that could benefit from either library, or both. I’m glad I finally took the time to dig through it.