On Thu, 20 Nov 2003 02:43:31 +0900, Sean O'Dell wrote:
> On Tuesday 18 November 2003 10:30 pm, Austin Ziegler wrote:
>> On Wed, 19 Nov 2003 08:08:25 +0900, Sean O'Dell wrote:
>>> It would also be nice if I could specify that certain method
>>> parameters can only be objects containing a certain interface.
>>> For example:
>>> 
>>> def mymethod(:SomeModule a) end
>>> 
>>> ...where Ruby automatically asserts for me that a has
>>> :SomeModule somewhere in its hierarchy, and throws an
>>> appropriate exception if it does not.
>> Blech. I don't want Java. You're talking exactly what Ryan's
>> StrongTyping module does, and assuming that a name accurately
>> represents an interface.
> Then don't specify a type for your arguments. Some people need
> it, some people don't.

Who *needs* it? And why? Immediately, the classes of applications
that *need* this are (1) RPC-based programs that are dynamically
discoverable, and therefore need cross-language inspection and (2)
interface builders (a la Visual Basic "Property Inspectors"). I
can't think of any other class of application that *needs* this sort
of meta data. I fully support being able to have this information
available, say:

    # mymethod expects :SomeModule and returns :String
  sig :mymethod {
    new_signature
    argument  [:a, :SomeModule]
    returns   :String
    new_signature
    argument  [:a, :OtherModule]
    returns   :Array
  }

  def mymethod(a); end

I wouldn't support static typing, which is precisely what your
mechanism does (and StrongTyping does, too). Ryan Pavlik has done
something similar to the sig mechanism, but I think that its
interface is unwieldy.

>> If you want to ensure something conforms to an expected
>> interface, test that interface. For example, adapted from
>> Text::Format:
>> 
>> def hyphenator=(x)
>>  raise unless x.respond_to?(:hyphenate_to)
>>  raise unless [2, 3].include?(x.method(:hyphenate_to).arity)
>>  @hyphenator = x
>> end
>> 
>> This would be simplified by my proposed change to #respond_to?
>> (although it wouldn't *really* help in this case; I'd need to
>> have an Array version).
> Doing a respond_to? for every method you need an object to
> implement is tedious,

Yes, it is. That's why I don't do it often. I document what the
object requires and then I expect the user to follow it. This is the
heart of duck-typing: you use the object given to you. Unless you're
doing something that requires programmatic discovery (see my notes
above), documentation is sufficient.

> especially when developing a collection of related functions, and
> it doesn't mean that the object does what it reports to do, nor
> that it takes the parameters you expect. For example, if an object
> responds_to?(:open), what does it open? A file by name?  The end
> of a socket?  Which?  However, if an object implements the :socket
> interface, then I know it responds_to? :open, :close:, :read,
> :write, :select, etc. and I know exactly what parameters to pass.

*shrug* Whatever. Your document would read something like:

  # ios:: ios must respond to #open, #close, #read, and #write.

> These things are important.

In statically typed languages, sure they are. In Ruby, less so.
Consider:

  irb(main):008:0> StringIO.ancestors
  => [StringIO, Enumerable, Data, Object, Kernel]
  irb(main):009:0> StringIO.new.respond_to?(:select)
  => true

Oops. StringIO instances respond to #select, but they're not IO or
File objects at all. Exactly how does name checking help you here?
StringIO is a valid IO object by signature, but it's not "of IO".

> It saves a lot of code and headaches when you can just ask "does
> this object implement this interface?"

Why do you need to ask unless you're implementing one of the cases
that I pointed out above? Look at a lot of Ruby code out there: it
*assumes* that the object provided implements the interface -- and
it documents it as such.

> You might not find it useful, but others definitely do.

Mostly people new to Ruby recently from Java. Seriously.

> This is one of the things I think Ruby could do very well and
> still retain its flexibility.

I personally disagree, at least with the ways that have been
suggested to date. The closest is Ryan's metadata documenting
mechanism, and I think that the interface is unwieldy to the point
of uselessness. Ancestry-checking isn't really useful to most of
how Ruby is used.

> Being able to query for the signature of an interface, with some
> syntactic sugar to allow parameters to require them (optionally)
> is very much, to me, The Ruby Way; fearless and visionary.  This
> should make everyone happy; people don't lose their flexibility
> and we get all the type checking anyone should need.

Once again: ancestry/name checking is not type checking. Neither is
method checking (#respond_to?). Ruby divorces type from class, which
is something that C++ doesn't do, and Java users "sorta" get if they
think about interfaces.

Anecdotally, I did some work with Ruwiki recently and Chad Fowler
created a mock object to make the Ruwiki tokenizing process think
that it was dealing with an instance of Ruwiki. That mock object has
become part of Ruwiki's object hierarchy, although not as Chad
defined it. (I abstracted the data that Chad was using into its own
object.) If I had done ancestry checking initially, Chad wouldn't
have been able to do this.

> So, although YOU might not need this, I and others do.

Why? I'm serious. It's also a rhetorical question: why do you need
this? What about your code demands this? Is it a presumed need, or a
real need? If it's a presumed need, YAGNI. If it's a real need, why
not consider if your design and/or implementation are too fragile. A
need for static typing in Ruby is an indicator of code smell. Yes, I
have some smelly code that I haven't updated to this point.

> I think this is also one of those little things that would win
> people over to Ruby. Having absolutely no solid type checking is a
> big turn-off for a lot of people, and it's what keeps them
> developing in C/C++, Java and so on. If they're contemplating
> learning a script language, this would definitely catch their eye.

If someone wants a scripting language that requires type checking,
use PHP. Neither Perl nor Python offer type checking[1], although
Python went through this discussion a couple of years ago, and they
apparently did decide to implement an optional type system. The best
form I found from my perspective was PyIDL.

  http://www.prescod.net/pytypes/

Looking over the Python 2.2 documents (this discussion was for
Python 1.6 in 1998), *nothing was done*.

Consider one of the bright lights in the Java industry's thoughts on
static/strong typing:
  http://www.mindview.net/WebLog/log-0025

Excepting the cases that I pointed out at the top of this response
(RPC discovery and object inspectors), YAGNI.

-austin
--
austin ziegler    * austin / halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.11.19
                                         * 15.04.36