On Mon, 17 May 2004, Tim Bates wrote:

> Below is my dissertation on the subject. :P My
> intention is to incorporate any comments people might have into the text
> and then place it on the Wiki as an introduction to Duck Typing for the
> static typist.

Excellent idea, Excellent article. Thanks.

> 1) People with a Static Typing background often have the urge to do
> something like this:
>
>    attr_reader :date
>    def date=(val)
>      raise ArgumentError.new("Not a Date") if val.class != Date
>    end
>
> This is not duck typing - this is trying to get Ruby to do Static Typing.

Well, a more sophisticated Static Typer would do...
    attr_reader :date
    def date=(val)
      raise ArgumentError.new("Not a Date") if val.kind_of? Date
    end

> 2.a) Discussing this on #ruby-lang, David Black suggested the following
> optimization:
>
>    def date=(val)
>      begin
>        @date = Date.new(val.year, val.month, val.day)
>      rescue
>        begin
>          val =~ /(\d{4})\s*[-\/\\]\s*(\d{1,2})\s*[-\/\\]\s*(\d{1,2})/
>          @date = Date.new($1.to_i,$2.to_i,$3.to_i)
>        rescue
>          begin
>            @date = Date.new(val[0], val[1], val[2])
>          rescue
>            raise ArgumentError, "Unable to parse #{val} as date"
>          end
>        end
>      end
>    end

Really cute. More polymorphic, probably a bit slow as exception paths
are usually expected to be "rare" and hence under
optimized. (Certainly true in C++, I don't know about Ruby.) Anybody
care enough to benchmark this?

I would also prefer it to rescue just a No Method exception than just
any Exception. You can really really really hide some horrible bugs catching
just any exception. Like empty catch blocks in Java, it is a truly
evil practice.

> 4) The fourth and final approach, which I believe to be the Zen of Duck
> Typing, is as follows:
>
>    # Accepts an object which responds to the +year+, +month+ and +day+
>    # methods.
>    attr_accessor :date

The worse breakage I have seen in a Object Oriented System was in a
language called Actor, where for optimization reasons they had forced
the internal representation of their graphics type into pairs of two byte
integers. This turned a very large, very useful generic 2D geometry
library into something totally useless to me. ie. Never gratiutously
break polymorphism, even if you personally can't think of another use
for this code, somebody else can.

For example, suppose you got a string from a SQL server that was a
"date". But since parsing the string is difficult and expensive to do,
you just don't. You stuff it into the date= method and lo and behold,
it just so turns out that for this execution path you _never_ actually
invoke :year, :month, :day, it just travels through your system, and
gets converted to a string with .to_s and written back to SQL.

If you had put additional checks in the code, it would have broken,
and you would have been forced to do an expensive and useless
conversion.




John Carter                             Phone : (64)(3) 358 6639
Tait Electronics                        Fax   : (64)(3) 359 4632
PO Box 1645 Christchurch                Email : john.carter / tait.co.nz
New Zealand

The universe is absolutely plastered with the dashed lines exactly one
space long.