Aspects of Beauty: Proportion, Integrity, Clarity, and Monkey Patching?

Posted by Rick DeNatale Mon, 20 Aug 2007 18:25:00 GMT


Besides being a master werewolf, Marcel Molina Jr. gives great presentations!


In his keynote presentation on the second day of the Ruby Hoedown, Marcel talked about
“What Makes Code Beautiful”,
click on the link for the confreaks video of this session.

The talk started with an exploration of the classical Philosophy of Beauty, from Plato to Descartes.
Marcel summarized this by proposing that beauty lies in the balance between three aspects which,
at times, either strengthen or oppose each other:

Proportion
The property that components have the appropriate(relative) size/weight.
Integrity
I would summarize this as “fitness of purpose.” Marcel’s anti-example was a
hammer made out of glass. Although it might be beautifully constructed, and a joy to the eye, it
would be unlikely to serve its intended purpose, and thus would fall short on integrity
Clarity

The property of being easily grasped as to meaning and function.

Beauty and Ruby

About 16 minutes into the talk, Marcel started talking about this view of beauty in the context of Ruby code. He gave an example of some really “clever” code to convert strings to an appropriate
instance of a Ruby class, for example “true” would me converted to true, “false” to false, and
strings representing integer or time values to Integers or Times, respectively.

The code in question, implemented a kind of functional language pattern match against the string.
Marcel suggested that he might have been into studying Haskell at the time he wrote this code.
He used a generator to produce an enumerable collection of patterns to try, and did some “nice”
tricks to allow the result of a pattern match to sometimes be solely the value he wanted, and
sometimes to be an array with the value as the second element, to handle the special case where the
desired value was the literal false. If it sounds complicated, it is, I’ve placed the code at
the end of this article.
Some of us in the audience, “smelled” this code right away.

He then critiqued this solution. Although he had originally considered it “beautiful” since it was “elegant” and “sophisticated” he came to smell it too.

A Fresh Design

Here’s how the code ultimately was written:

class CoercibleString < String
  def coerce
    case self
    when 'true':          true
    when 'false':         false
    when /^\d+$/:         Integer(self)
    when datetime_format: Time.parse(self)
    else
      self
    end
  end
end

Once this much simpler design is unveiled the original “sophisticated”, and “elegant” design
looks anything but.

Measuring against Proportion, Integrity, and Clarity

Proportion
The original is a total failure, it’s much too long compared to the final
code.
Integrity
Again the original loses on this aspect. The use of the generator, particularly
the early continuation based implementation, causes very slow performance, and leaks memory. Marcel
stated that the simpler version is an order of magnitude faster.
Clarity
Do I really have to explore this?

Marcel had pointed out when describing the original design that one of it’s “cool features” was
extensibility. Adding a new coercion just required adding another call to try in the Generator.new
block.

In contrast adding a new coercion to the better design just requires adding a when leg to the
case statement.

The Questionable Beauty of Making Subclasses of Core Classes

While I loved the talk and agree with 99 and 44/100%, I’m just a bit troubled by the introduction
of the CoercibleString class. I think that it falls down on proportion at least.


It seems to me that there’s some missing code here. How do you actually coerce a string.
This seems to strongly imply a usage like this:

class PayloadProcessor

  def process
    # code which extracts a string to be coerced

    #coerce the string referenced by the variable value_str
    value = CoercibleString.new(value_str).coerce
    
    #further processing
  end
end

An alternative, and it seems to me to be a better one, although I’m convinceable otherwise, would
be to just make that method part of the class requiring the conversion, either directly, or through a
module:

class PayloadProcessor

  def process
    # code which extracts a string to be coerced

    #coerce the string referenced by the variable value_str
    value = coerce(value_str)
    
    #further processing
  end

  def coerce(str)
    case str
    when 'true':          true
    when 'false':         false
    when /^\d+$/:         Integer(str)
    when datetime_format: Time.parse(str)
    else
      str
    end
  end

end

Now some might argue that the ‘functional’ looking coerce method which takes the string
as an argument rather than the receiver seems somehow less ‘object oriented’, but I find this
unconvincing.

If CoercibleString is a class we need code to create it from a string, something like:

  class CoercibleString < String
    # Create a new coercible string
    # Note that since the actual value of
    # Ruby strings are not held by an instance variable
    # we need to alter the internal representation
    def initialize(source_str)
      self << source_str
    end
  end

I had a brief conversation with Marcel about whether or not subclassing string really seemed
appropriate, but it lasted all of about a minute. There’s a bit of supposition here on my
part, so apologies to Marcel if I misunderstood the exchange. He indicated that he would probably
advocate
defining a method called CoercibleString, in parallel with Kernel#Integer and its ilk.

module Kernel
  def CoercibleString(str)
     CoercibleString.new(str)
  end
end

But this syntactic sugar, just seems to be tilting the balance towards a less proportional design.

Conclusion

Building new classes is often a good idea, but not always. I’m not totally convinced that
coerce(str) is more beautiful than CoercibleString.new(str).coerce, or CoercibleString(str).coerce,
but my sense of esthetics tilts me that way.

Comments?

A “Smelly” Way to Coerce Strings

Here’s Marcel’s original code:

class CoercibleString < String
  attr_accessor generator

  def coerce
    attempt = nil
    break unless {attempt = coercions.next).nil? while coercions.next?
    attempt.nil? ? self : attempt
  end

  private
    def coercions
      Generator.new do | self.generator |
        try { self == 'true'             }
        try { [self == 'false', false ]  }
        try { Integer(self)              }
        try { Date.parse(self)           }
      end
    end

    def try
        attempt, desired = yield
        generator.yield(desired.nil? ? attempt : desired) if attempt
    rescue ArgumentError
        generator.yield nil
    end
end

Ruby Hoedown TeeVee - "Hee Haw!"

Posted by Rick DeNatale Mon, 20 Aug 2007 14:13:00 GMT


I notice that master werewolves
Coby Randquist and Carl Youngblood
have gotten all of the
videos from the
inaugural Ruby Hoedown
(at least
in rough cut form) on their confreaks website.

I’m stoked about this, and the site in general.
Like the way infoq web publishes presentations, confreaks
shows synchronized live action video and the “slides”, however I like the confreaks site better.



  • The “slides” are directly captured as video from the presenters laptop. The big plus here is that
    live demos are also there.

  • The presentation is “widescreen” with the two components laid out side by side, which I think
    works better.

As for content, there was lots of good stuff at the hoedown which is now available for viewing on-line. Check it out.


After the Hoedown is Over, Part 1

Posted by Rick DeNatale Tue, 14 Aug 2007 15:03:00 GMT


The first Ruby Hoedown, sponsored by the Raleigh Ruby brigade, has finally come and gone. I had a great two days, saw some old friends, and made lots of new ones.

Here are my initial thoughts after more than a bit too litle sleep after a night trying to root out werewolves.

Deja-Vu All over again

I was just a little surprised how often Smalltalk came up during the weekend.

  1. The first instance was in the keynote talk by Bruce Tate, well known as a Java apostate in which he explored the current state of Ruby, how it got where it is, and where it might go in the future.

    This was in part a cautionary tale, and the fate of Smalltalk in the mid-late 1990s loomed large.

    As one of those heavily involved in the Smalltalk community as a developer and evangelist for IBM, I found it interesting to get an independent perspective.

  2. Ken Auer gave a talk on Saturday called “Does Ruby have a Chasm to cross?”
  3. which covered much of the same ground as Bruce’s talk, but really focused on comparing the life paths of Ruby and Smalltalk.

    So another independent perspective on what happened to Smalltalk, this time from someone with known credentials as a key member of the Smalltalk community when it was booming. Ken and I go back quite a ways.


    Ken worked at Knowledge Systems Corp back then, which as a company which provided intensive training in object oriented development using Smalltalk via an apprentice program. Clients would send small development teams to KSC to work on the clients project with the assistance and mentoring of KSC folks. Efforts such as these were important seeds for what we now know as the agile movement.


    Ken got a lot of things right about what happened to Smalltalk, although I could add some details and corrections from and “inside IBM” perspective, which might be food for future articles.

  4. Finally, I “organized” a birds-of-a-feather session covering “Ruby for Smalltalkers, and Smalltalk for Rubyists” which was well attended and turned out to be mostly a few old IBM Smalltalkers like me, Fred George, and my
    “evil twin”, Pat Mueller
    responding to questions from Rubyists about Smalltalk.

Testing Workshop

Stepping back, on Friday morning, Bruce Tate,
Chad Fowler
, and
Marcel Molina jr. ran a charity workshop on test driven development, with proceeds going to the local food bank.

Although I’m a pretty committed test-driven developer, I found the session very informative, and got a new appreciation for the use of mocks, not only to allow testing before all the code is written, but to keep tests independent even after the components being mocked exist.


Watch this space


This article has already gotten long enough, I expect to post more about the events of the past weekend in the near future.