Erudition and Inanity

brian guthrie blogs

I'm Not Sure That Word Means What You Think It Means

Posted by Brian Guthrie Fri, 15 Aug 2008 20:17:00 GMT

Potential customers can be hard to come by, especially since you’re building something that nobody will ever, EVER want. Well, that’s getting ahead of myself. Let’s just agree that when you’re setting out to Gather Business Requirements, your potential customers usually aren’t already sitting in the room with you. [link]

I’m not sure what to say. At the intersection of people who need shit built but can’t program and people who know how to build shit but don’t know the domain you have business requirements. Steve Yegge seems to think that business requirements are something you gather using focus groups, and loathes them; on the types of projects I work on, we typically gather them from the domain experts and we build them for people who need shit built desperately badly. The fact that they grow out of line with what you need is an immediate consequence of the fact that his teams have tried to Do It All Up Front rather than iterate and improve, and this coming from a man who loathes Agile but just as clearly doesn’t understand that any better than he understands business.

A curious, inquisitive, and passionate person can find the passion in virtually any domain they’re asked to turn their keyboards to; in the past year I’ve had the opportunity to work on a wide variety of projects in areas that I never thought I’d enjoy. It’s sad to see that Yegge seems to be advocating a kind the kind of intellectual blinders that confine you and your work to the things you’re already interested in and understand, all the more because it seems to run counter to a lot of what he’s written so eloquently about in the past.

Posted in | no comments |

GitHub, briefly

Posted by Brian Guthrie Mon, 21 Jul 2008 04:54:00 GMT

I know that I’m late to the party here, that all of the cool kids have had accounts for what seems like Internet-decades, and that I can do nothing but add my voice belatedly to an already rapturous chorus; nevertheless, forgive my enthusiasm: GitHub rules, it is worth joining, and, once signed up, it took only a week for my workflow to be forever improved. I am, in short, a believer.

  • It’s never been easier or more convenient to contribute to other open source projects.
  • It’s never been easier to work remotely.
  • It’s never been easier to track the progress of projects and developers you’re interested in.
  • Unicorns! Rainbows! Puppies!

Over the next few weeks I’ll gradually be migrating all of my projects over to it, and I encourage you to do the same.

Posted in | no comments |

Handshake 0.2.1 Released

Posted by Brian Guthrie Wed, 02 May 2007 16:45:00 GMT

I’ve pushed out a new version of Handshake. The big-ticket item in 0.2 is that method argument contracts now support contracts on blocks. When a block is passed to a method that’s protected by a block contract, the block will be modified to check the arguments and return values for that block in much the same way that a normal contract is checked. Note that this will probably not work in Ruby 1.9, or at least will have to be modified for recursive contract checking.

Here’s the syntax:

class StringArray < Array
  contract :each, Block(String => anything) => self
end

Block checking in 0.2.0 involved reopening the Proc class; release 0.2.1 fixes that.

The new release also contains a new method, checked_self. Because of the way Handshake is implemented (with a proxy object), calls to private methods aren’t checked (because they’re called on the real object instead of the proxy object). checked_self returns the proxy object instance and calls made to that object will be contract checked.

I’ve also improved the documentation. You can find the rdoc at http://handshake.rubyforge.org, and the project page at http://rubyforge.org/projects/handshake/.

Posted in | no comments |

Let's make a deal: Handshake, a contract system for Ruby

Posted by Brian Guthrie Tue, 20 Mar 2007 16:38:00 GMT

Handshake is an informal design by contract system written in pure Ruby. It’s designed to offer simple, flexible semantics for improving the clarity and robustness of your code. It supports three basic kinds of checking: invariants, method pre/post conditions, and method arguments. This is a unit-tests-pass-but-you-probably-shouldn’t-use-it-for-anything-important 0.1 release.

A couple of clarifications: Pre- and post-conditions, and argument contracts, all accept the name of the method you’d like them to apply to as an optional first argument. If you omit it, they’ll be applied to the next method you define.

Also, anything is a clause that always returns true.

Updated 5/9/07: I’ve modified some of the content below to reflect changes to Handshake’s interface. Handshake is also now obtainable as a gem (gem install handshake).

Argument Contracts

Argument contracts are insanely handy. They allow you to specify conditions for the arguments and return values of methods simply and easily. Here’s an example:

class AcceptsString
  include Handshake
  contract [ String, Fixnum ] => anything
  def initialize(str, num); ...; end
end

Wait a second, I hear you say. That’s not exciting at all! I just left Java behind forever. Quasi-statically-typed languages are for suckers.

Here’s the rub: Handshake accepts as an argument clause any object that implements the === method. Why? Because it’s an incredibly convenient “is equivalent to” form of comparison. It’s what makes Ruby’s case statement so handy. Try this awkwardly contrived example on for size:

# Rate a website on a scale from 1 to 5.
class WebsiteRatingSystem
  include Handshake

  contract [ String, /^http:\/\// ] => anything
  def initialize(name, url)
    @name, @url, @ratings = name, url, []
  end

  contract 1..5 => anything
  def add_rating(n); @ratings << n; end

  contract nil => 1..5
  def average_rating
    ratings_sum = @ratings.inject(0.0) { |sum, n| sum + n }
    ratings_sum / @ratings.length
  end

  contract [ String, [ Integer ], Block([String, Integer] => anything) ] => Hash
  def generate_report(name, *metrics, &block)
    ..
  end
end

But Brian, you say, in the imaginary conversation I am conducting with you in my head. Brian, I like being able to give it a range, or a regexp. That seems downright neato. But man, what a hassle to have to craft my own ===-implementing object for every single clause.

As it turns out, I have made this process entirely hassle-free for you.

contract assert("equals foo") { |arg| arg == "foo" } => anything
contract any?(String, Integer) => anything
contract [ all?( hash_of?(Symbol, String), 
                 hash_with_keys(:foo, :bar, :baz) ] => anything
contract nonzero? => hash_contract(:count => Integer, :description => String)
contract responds_to?(:each, :map, :length) => anything
contract nil => String

And they said that duck typing and contracts didn’t mix! Good day, sir and/or madam.

Except for also

I’ve defined some accessors, just for you.

class HoldsSomeThings
  include Handshake
  contract_accessor :foo => String, :bar => Integer
  contract_reader ...
  contract_writer ... # You get the idea.
end

Invariants

Also handy are invariants, which are conditions that should always hold. They’re checked after your object is constructed and before and after every method invocation. They have access to local methods and instance variables. They are all up in your class, policing your object.

class NonEmptyArray < Array
  include Handshake
  invariant { not empty? }
end

Pre- and post-conditions

Sometimes you need a broader, more holistic approach to method checking. These conditions, if specified, are executed before and after (respectively) the named method and have access to its arguments, return value(s), and any local methods and instance variables. These work very much like you’d expect them to:

class HoldOnJustAMinute
  include Handshake

  before("halt in the name of the law") do |arg|
    assert @justice_dispensable
    assert arg.law_abiding?
  end; def do_something_potentially_damaging(arg)
    ...
  end
end

Caveats

  • It isn’t underpinned by a formal calculus (although it’s modeled very loosely on the PLT Scheme contract library and on the Eiffel contract system). I’m more or less cool with that; Ruby’s appeal to me comes not least from its loose informalism. But the professor under whose grudging tutelage I developed this would like you to know that until it does it’s not good for anything whatsoever, and whatever benefits you think you might be deriving from such a seemingly useful library are just a figment of your diseased imagination. He informs me that he’s okay with that bit, but the half-bakedness of the implementation bothers him more. See below.
  • It doesn’t yet have a proper blame system. See abovebelow re: professor.
  • It handles inheritance only trivially: a subclass will inherit the contracts of its superclass but will not prevent you from overriding them. This means that it allows you to write contracts that fail the Liskov substitution principle. I hope to fix this soon, but in the meantime, don’t let it keep you awake at night.
  • There’s a lot of metaclass magic going on under the covers, and you shouldn’t use it in contexts with a lot of other metaclass magic (myrailsapp/lib is probably fine; myrailsapp/app is not.)
  • There’s no rdoc yet (although the code itself is fairly well-documented). Fixed. See handshake.rubyforge.org.
  • You can’t define contracts in modules.
  • And it will probably impact performance, although I haven’t conducted any performance testing.

Update: The inimitable Matthias Felleisen responds via email:

1. I never insist on a formal calculus. That’s what researchers are for. It’s a tool for exploring the ideas, communicating them in concise form, and proving basic fundamental theorems about such frameworks.

2. I do think that half-baked or quarter-baked implementations of good ideas are bad for the construction of quality software in the long term. They just propagate the silly idea that programming is shallow and anyone can do it, trained or not. One day we’ll have the equivalent of bridges come down on us in software and then the regulators will be worse than anyone can imagine now.

3. I also believe that without a properly design blame system, a contract framework provides only a 1/3 of the value that it can. That’s sad. It’s like buying a model T when we know how to build Porsches.

In my defense, this is at least two-thirds more-baked than any other Ruby contract system (5/9/07: except flgr’s ruby-contract. But I still like mine better). Since no Ruby contract system that I’ve seen is more than 15% baked, I guess that would make this… yes, about a quarter baked. It’s also a lot of fun to work on.

Finally

Find the project online at rubyforge.org/projects/handshake (MIT license), the RDoc at handshake.rubyforge.org, and the gem handshake, and please do let me know if you end up using it, and how.

no comments |