Duck typing and the Standard Library

Why is it bad practice to check the type of a value at runtime? I'm new
to Ruby, but I thought that I had read that it is dynamically, strongly
typed, meaning that variables do not have types but values do. I would
think that it would be common to check types at run time.

I would think that checking types of values would be part and parcel of
good object-oriented programming. Doesn't OO insist that classes are
more than the sum of their methods? Does duck-typing contradict this?
Does it expect us to treat the following classes as
assignment-compatible?

class DatabaseConnection
  def open()
    #...
  end

  def close()
    #...
  end

  # Returns true if the connection has been opened.
  def check()
    # ...
  end
end

class BankAccount
  def open()
    #...
  end

  def close()
    #...
  end
  
  # Returns a Check object with a new check number.
  # Fails if open has not been called.
  def check()
    #...
  end
end

The contracts of these classes are totally different.

···

-----Original Message-----
From: dblack@rubypal.com [mailto:dblack@rubypal.com] On Behalf Of
dblack@wobblini.net
Sent: Tuesday, October 31, 2006 6:11 AM
To: ruby-talk ML
Subject: Re: Duck typing and the Standard Library

Hi --

On Tue, 31 Oct 2006, mikeharder@gmail.com wrote:

Robert Klemme wrote:

On 31.10.2006 10:19, mikeharder@gmail.com wrote:

I'm new to Ruby, so please excuse any ignorance on my part. I read

the

following article about Ruby and "duck typing":

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/100511

I got the impression that duck typing is the "right" way to do

things

in Ruby. However, the Ruby Standard Library itself doesn't seem to

use

duck typing. Consider the following example:

irb(main):001:0> require 'set'
=> true
irb(main):002:0> s = Set.new
=> #<Set: {}>
irb(main):003:0> s.superset? 0
ArgumentError: value must be a set

It seems like the "superset?" method explicitly checks that its
parameter is a set. This is approach #1 in the "duck typing"

article

above, which the article claims is not "the Ruby way".

So, what gives? If it's wrong to "try to make Ruby do Static Typing"
(as the article says), then why does the Ruby Standard Library do

it?

It is just giving you a nicer error message. Otherwise, this might

happen:

irb(main):003:0> def foo(s) s=s.to_set end
=> nil
irb(main):004:0> foo
=> #<Set: {}>
irb(main):005:0> foo Set.new
=> #<Set: {}>
irb(main):006:0> foo 0
NoMethodError: undefined method `to_set' for 0:Fixnum
         from (irb):3:in `foo'
         from (irb):6

Note that there must be an exception of some kind if the parameter is
inappropriate. And no, this is not static typing.

Kind regards

  robert

Yes, there must be an exception of some kind if the parameter is
inappropriate. The question is whether to explicitly check the type

of

the parameter and throw a nicer exception, or to explicitly *not*

check

the type of the parameter and let the exception happen where it may
(i.e. when a needed method does not exist).

Arguments can be made for both sides, but which is considered the

"best

practice" for Ruby development? The Ruby Standard Library suggests it
is the former, but the article I referenced above suggests it is the
latter.

The way I've always looked at it is that the Ruby core and standard
library do a certain number of things along these lines that are
necessary for bootstrapping the basic classes into existence and
dealing with their relations, but that aren't necessarily what you'd
do normally when using those classes. You get a lot more "type"
errors (most of which are actually class errors) from the built-ins
than from most other code.

The main disadvantages of testing for class/module ancestry are:

   1. it doesn't actually guarantee an object's behavior;
   2. it discourages thinking about more flexible ways of programming.

I think that in the case of some of the standard and core classes,
these concerns don't loom as large as they might in our programs. At
the same time, there's no particular reason for those classes *not* to
be more duck-type oriented, if that can be done without introducing
any problems.

David

--
                   David A. Black | dblack@wobblini.net
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org
****************************************************************************
This email may contain confidential material.
If you were not an intended recipient,
Please notify the sender and delete all copies.
We may monitor email to and from our network.
****************************************************************************

Hi --

Why is it bad practice to check the type of a value at runtime? I'm new
to Ruby, but I thought that I had read that it is dynamically, strongly
typed, meaning that variables do not have types but values do. I would
think that it would be common to check types at run time.

It depends what you mean by checking type. In Ruby, class and type
are two different concepts: an object's class is what you get when you
say: obj.class, while its type is a sort of after-the-fact
characterization of its capabilities.

Every Ruby object can have its own runtime profile (type). That has
certain implications. Checking an object's class is often a good
indicator, but never a guarantee, of the object's behavior. At a more
fine-grained level, you can check to see whether an object is aware of
a given method, using respond_to?, before you call the method. This
comes closer to checking the object's type.

I would think that checking types of values would be part and parcel of
good object-oriented programming. Doesn't OO insist that classes are
more than the sum of their methods? Does duck-typing contradict this?

Does it expect us to treat the following classes as
assignment-compatible?

class DatabaseConnection
def open()
   #...
end

def close()
   #...
end

# Returns true if the connection has been opened.
def check()
   # ...
end
end

class BankAccount
def open()
   #...
end

def close()
   #...
end

# Returns a Check object with a new check number.
# Fails if open has not been called.
def check()
   #...
end
end

The contracts of these classes are totally different.

The idea(l) of duck typing would be that you simply send a message to
an object, and let the object handle the message. The kind of
synonymy you're demonstrating is certainly something you have to be
aware of, though. In practical terms, however, the likelihood of
there being a problem is equal to (or less than) the likelihood of
someone's doing this:

   dbc = BankAccount.new

when they meant to do this:

   dbc = DatabaseConnection.new

which is pretty slim. And if that can happen, then this can happen:

   dbc = DatabaseConnection.new
   def dbc.close
     dbc.command("drop database...")
   end

etc. In other words, the fact that an object reports its class as
DatabaseConnection doesn't guarantee the result of calling its
methods, either.

David

···

On Wed, 1 Nov 2006, Hartin, Brian wrote:

--
                   David A. Black | dblack@wobblini.net
Author of "Ruby for Rails" [1] | Ruby/Rails training & consultancy [3]
DABlog (DAB's Weblog) [2] | Co-director, Ruby Central, Inc. [4]
[1] Ruby for Rails | [3] http://www.rubypowerandlight.com
[2] http://dablog.rubypal.com | [4] http://www.rubycentral.org