"stereotyping" (was: Re: Strong Typing (Re: Managing metadata about attribute types) )<Pine.LNX.4.44.0311171402340.1133-100000@ool-435 5dfae.dyn.optonline.net>

I share some of David’s concerns, but would like to point out that not only
does Class name checking not ensure needed behavior (which is true, but a
small danger in my mind), it overly constrains the software to reject
perfectly good solutions.

Consider the following function.

def read(io_object)
fail “Not an IO Object” unless IO === io_object
io_object.read
end

This seems like a perfectly reasonable function and we feel safe because
carefully check the “type” (actually Class ancestry) of our parameter.

Now consider the following usage:

io = StringIO.new(“HI”)
read(io) # => RuntimeError: Not an IO object

This perfectly reasonable use of read will fail because StringIO does not
enherit from IO, even though it implements IO-like methods. This is a
shame. Our “type-checking” has needlessly constrainted our solution.

That is the danger I see with a lot of the type checking solutions proposed.
Its not that people extend objects, but that they make the software brittle
and less flexible.

Just my two cents.

···

David Black (dblack@wobblini.net) wrote:

Class name checking doesn’t ensure needed behavior. Actually, let me


– Jim Weirich / Compuware
– FWP Capture Services
– Phone: 859-386-8855

I think this is a flaw in the current ruby libraries, not a flaw in the
concept of typechecking.

There can be an interface defining what it means to “be an IO”. Any
object which does in fact behave exactly in accordance with this
contract should mark itself as being an implementation of that
interface.

I suggest this approach:

an IO object fulfils the following contract:

… details here …

module IO; end # note: no implementation!!!

class StringIO

declare that this class fulfils the IO contract

include IO

end

class File

declare that this class fulfils the IO contract

include IO

end

Now someobj.is_a?(IO) will return true for all those objects which
fulfil the IO contract. Note that the IO module does not have any
implementation; it is purely an abstract concept and therefore can be
mixed in to any existing class. When mixed in, it functions as a promise
of behaviour which can be checked for at runtime.

I expect that some people will put forward the idea that some class
might be invented that happens to also fulfil the IO contract without
being explicitly marked so, and that checking for the “IO” marker would
disallow passing this class. Well I think the chances of a class
happening to exactly fulfil a contract without that contract ever have
being taken into consideration is smaller than the chance of us all
being wiped out by an asteroid.

Note that checking for concrete classes using is_a is a totally
different issue. That does present problems regarding the inheritance
tree, because you can’t just mix in such a type to any other type. I
think the arguments against this pattern are on much stronger ground.

Comments?

Simon

···

On Wed, 2003-11-19 at 10:30, Weirich, James wrote:

David Black (dblack@wobblini.net) wrote:

Class name checking doesn’t ensure needed behavior. Actually, let me

I share some of David’s concerns, but would like to point out that not only
does Class name checking not ensure needed behavior (which is true, but a
small danger in my mind), it overly constrains the software to reject
perfectly good solutions.

Consider the following function.

def read(io_object)
fail “Not an IO Object” unless IO === io_object
io_object.read
end

This seems like a perfectly reasonable function and we feel safe because
carefully check the “type” (actually Class ancestry) of our parameter.

Now consider the following usage:

io = StringIO.new(“HI”)
read(io) # => RuntimeError: Not an IO object

This perfectly reasonable use of read will fail because StringIO does not
enherit from IO, even though it implements IO-like methods. This is a
shame. Our “type-checking” has needlessly constrainted our solution.

And moreover you could reopen the class and mixin IO.

martin

···

Simon Kitching simon@ecnetwork.co.nz wrote:

I expect that some people will put forward the idea that some class
might be invented that happens to also fulfil the IO contract without
being explicitly marked so, and that checking for the “IO” marker would
disallow passing this class. Well I think the chances of a class
happening to exactly fulfil a contract without that contract ever have
being taken into consideration is smaller than the chance of us all
being wiped out by an asteroid.

Class name checking doesn’t ensure needed behavior. Actually, let
me

I think this is a flaw in the current ruby libraries, not a flaw in
the concept of typechecking.

There can be an interface defining what it means to “be an IO”. Any
object which does in fact behave exactly in accordance with this
contract should mark itself as being an implementation of that
interface.

I suggest this approach:

[interfaces]

Now someobj.is_a?(IO) will return true for all those objects which
fulfil the IO contract. Note that the IO module does not have any
implementation; it is purely an abstract concept and therefore can be
mixed in to any existing class. When mixed in, it functions as a
promise of behaviour which can be checked for at runtime.

I expect that some people will put forward the idea that some class
might be invented that happens to also fulfil the IO contract without
being explicitly marked so, and that checking for the “IO” marker
would disallow passing this class. Well I think the chances of a class
happening to exactly fulfil a contract without that contract ever have
being taken into consideration is smaller than the chance of us all
being wiped out by an asteroid.

Note that checking for concrete classes using is_a is a totally
different issue. That does present problems regarding the inheritance
tree, because you can’t just mix in such a type to any other type. I
think the arguments against this pattern are on much stronger ground.

Comments?

I’ve been playing with a system like this - it allows the creation of
specialised modules which define a relatively strict contract which is
enforced at the time they are mixed in to a class.

e.g.
Interface.define(“Enumerable”)
module Interface::Enumerable
needs :each
optional :min, :max
include ::Enumerable
end

An interface can define prerequisite methods (both instance and
singleton methods) as well as optional methods. It is then mixed into a
class as normal, but only after the required methods have been defined.
It checks that the required functions have been defined, but also checks
for optional functions and preserves them if they already exist.

So, for the above example, using Interface::Enumerable could allow an
array class to use the brute force Enumerable#max, but wouldn’t prevent
the Range class from implementing a much simpler and faster Range#max.

Commments?

Geoff.

interface.rb (3.01 KB)

···

On Wed, Nov 19, 2003 at 07:06:24AM +0900, Simon Kitching wrote:

On Wed, 2003-11-19 at 10:30, Weirich, James wrote:

David Black (dblack@wobblini.net) wrote:

This was my thinking as well. I don’t like the idea of strong typing at all
in Ruby, but I do very much like the idea of being able to simply ask (or
assert that) object.is_a?(:id) and get an answer that basically says “this
object has :id somewhere in its hierarchy, either because its class is a
superclass of :id or :id is a module that was mixed-in at some point.”

All I ever really care about is knowing whether or not an object implements a
certain interface, and that interface can either be described as a mix-in
module or an ancestor class. It doesn’t really matter to me where that
interface was defined, any class or module will do; the object could also be
much more evolved than I expect it to be, and could even have lots of methods
overridden. I don’t really care. All I care about is if it is at least
implementing a certain basic interface.

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.

In fact, it would be also great if, instead of having to derive from or mix-in
an interface, I could just say:

class MyClass
interface :SomeModule
end

…which would act as though :SomeModule were mixed-in, except it’s basically
lying. If the author of MyClass didn’t really implement the interface
described my :SomeModule, KA-BOOM. But oh well, such is life. I like the
combination of “can do” with “can do it wrong.” It’s freedom.

In this way, some modules can be purely for interface describing. You could
make phony modules whose only purpose is to describe the interface, then
write classes that implement the interface and declare themselves as such.

This would be very, very nice to have.

Sean O'Dell
···

On Tuesday 18 November 2003 02:06 pm, Simon Kitching wrote:

There can be an interface defining what it means to “be an IO”. Any
object which does in fact behave exactly in accordance with this
contract should mark itself as being an implementation of that
interface.

I suggest this approach:

an IO object fulfils the following contract:

… details here …

module IO; end # note: no implementation!!!

class StringIO

declare that this class fulfils the IO contract

include IO

end

class File

declare that this class fulfils the IO contract

include IO

end

Now someobj.is_a?(IO) will return true for all those objects which
fulfil the IO contract. Note that the IO module does not have any
implementation; it is purely an abstract concept and therefore can be
mixed in to any existing class. When mixed in, it functions as a promise
of behaviour which can be checked for at runtime.

I expect that some people will put forward the idea that some class
might be invented that happens to also fulfil the IO contract without
being explicitly marked so, and that checking for the “IO” marker would
disallow passing this class. Well I think the chances of a class
happening to exactly fulfil a contract without that contract ever have
being taken into consideration is smaller than the chance of us all
being wiped out by an asteroid.

Note that checking for concrete classes using is_a is a totally
different issue. That does present problems regarding the inheritance
tree, because you can’t just mix in such a type to any other type. I
think the arguments against this pattern are on much stronger ground.

This perfectly reasonable use of read will fail because StringIO does not
enherit from IO, even though it implements IO-like methods. This is a
shame. Our “type-checking” has needlessly constrainted our solution.

Simon Kitching wrote:

I think this is a flaw in the current ruby libraries, not a flaw in the
concept of typechecking.
[…]
I suggest this approach:
[… redesign of Ruby class libraries to support abstract IO objects
elided …]

An interesting suggestion, but I still feel it still over constrains the
problem. For example, what if I had an input only device. If I include
the IO module then I am lying about its capabilities, but if I don’t
include IO, then I can’t use it in functions that read from IO objects.

Over constraint.

I suppose we can redesign the IO libraries again to reduce it to InputIO
and OutputIO classes, but then I would ask about sequential IO vs random
access IO. The logical conclusion of this exercise is a plethora of IO
modules, each covering a small subset of the IO capability. Look at the
Java IO library for an example of that.

Note that checking for concrete classes using is_a is a totally
different issue. That does present problems regarding the inheritance
tree, because you can’t just mix in such a type to any other type. I
think the arguments against this pattern are on much stronger ground.

Comments?

I will agree that abstract module markers are not as onerous as testing
for concrete classes, but they still carry significant design overhead,
impose a runtime overhead and still do little to actually guarantee that
the desired behavior actually exists.

···

On Wed, 2003-11-19 at 10:30, Weirich, James wrote:


– Jim Weirich jweirich@one.net http://onestepback.org

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

Geoff Youngs wrote:

So, for the above example, using Interface::Enumerable could
allow an array class to use the brute force Enumerable#max,
but wouldn’t prevent the Range class from implementing a much
simpler and faster Range#max.

Whats the improvement here. We can already fine tune
included method for each ``includy’’

class Range
def max
unless exclude_end?
self.end if self.begin <= self.end
else
# the super refers to Enumerable#max
super if self.begin <= self.end
end
end
end

but maybe I missunderstood something(s)?

/Christoph

This was my thinking as well. I don’t like the idea of strong
typing at all in Ruby, but I do very much like the idea of being
able to simply ask (or assert that) object.is_a?(:id) and get an
answer that basically says “this object has :id somewhere in its
hierarchy, either because its class is a superclass of :id or :id
is a module that was mixed-in at some point.”

That can be useful, but it tells you less than you think. As Jim
Weirich pointed out, it introduces fragility.

All I ever really care about is knowing whether or not an object
implements a certain interface, and that interface can either be
described as a mix-in module or an ancestor class. It doesn’t
really matter to me where that interface was defined, any class or
module will do; the object could also be much more evolved than I
expect it to be, and could even have lots of methods overridden. I
don’t really care. All I care about is if it is at least
implementing a certain basic interface.

Ancestry/name checking doesn’t tell you that a method implements a
certain interface. It says that it inherits from a class or includes
a module. You can assume that a name indicates an interface, but
that’s a bad assumption:

class File
class << self
undef :read
end
end

Oops! Now you’ve got something that’s a File, but doesn’t correspond
to the required interface. Granted, this is a pathological case, but
I could see this sort of thing being done in a sandbox situation.

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.

In fact, it would be also great if, instead of having to derive
from or mix-in an interface, I could just say:

class MyClass
interface :SomeModule
end

This is easy enough to implement, since interface would simply be a
private class or module method.

Ultimately, I don’t see a real value add with any of these
proposals. (Either the empty “interface” modules or the above. The
empty “interface” modules wouldn’t work for IO in any case, since IO
is a class as well.)

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).

-austin

···

On Wed, 19 Nov 2003 08:08:25 +0900, Sean O’Dell wrote:

austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.11.19
* 01.12.05

Sean O’Dell wrote:

Note that checking for concrete classes using is_a is a totally
different issue. That does present problems regarding the inheritance
tree, because you can’t just mix in such a type to any other type. I
think the arguments against this pattern are on much stronger ground.

This was my thinking as well. I don’t like the idea of strong typing at all
in Ruby, but I do very much like the idea of being able to simply ask (or
assert that) object.is_a?(:id) and get an answer that basically says “this
object has :id somewhere in its hierarchy, either because its class is a
superclass of :id or :id is a module that was mixed-in at some point.”

All I ever really care about is knowing whether or not an object implements a
certain interface, and that interface can either be described as a mix-in
module or an ancestor class. It doesn’t really matter to me where that
interface was defined, any class or module will do; the object could also be
much more evolved than I expect it to be, and could even have lots of methods
overridden. I don’t really care. All I care about is if it is at least
implementing a certain basic interface.

Yep. That’s my thinking as well. And it is possible in Ruby because of
its dynamics - one could create these interfaces and adapt to existing
object at runtime - thus allow for dynamic qualification of complex
behaviour. Give us both the flexibility and safety of type checking.
Of course, after checking, it could change too - but that’s a different
issue…

In this way, some modules can be purely for interface describing. You could
make phony modules whose only purpose is to describe the interface, then
write classes that implement the interface and declare themselves as such.

This would be very, very nice to have.

Second that :).

Thien

···

On Tuesday 18 November 2003 02:06 pm, Simon Kitching wrote:

Ruby definitely needs a solid, abstract IO interface.

But as to the issue of partially-implemented interfaces, I think they’re fine.
One IO interface is all you need. If you have an object that is input-only
and sequential, and the base IO interface allows for more, then your class is
simply not going to implement them.

Interfaces don’t need to be assumed fully-functional. Just partially
implement the interface. Objects of that class simply cannot be passed to
functions that do random-access IO or output. Even in strong-typing
environments like Java and C++, this happens all the time; as humans, we can
deal with it. =)

Sean O'Dell
···

On Wednesday 19 November 2003 05:09 am, Jim Weirich wrote:

On Wed, 2003-11-19 at 10:30, Weirich, James wrote:

[… redesign of Ruby class libraries to support abstract IO objects
elided …]

An interesting suggestion, but I still feel it still over constrains the
problem. For example, what if I had an input only device. If I include
the IO module then I am lying about its capabilities, but if I don’t
include IO, then I can’t use it in functions that read from IO objects.

It wasn’t a particularly good example (but just checking the code, a
good example wouldn’t have worked properly anyway :frowning: - line 87 has a
bug). But the purpose was to mainly to add consistency to the
following:

class Foo
def bar
puts "Foo#bar"
end
end

class Bar < Foo

end

module Iface
def bar
puts "Iface#bar"
end
end

If Foo has Iface mixed in, then Bar#bar will call Foo#bar, but if Bar
has Iface mixed in (and Foo doesn’t already), then Bar#bar maps to
Iface#bar.

The purpose was to provide a more consistent way of not overriding
existing methods when including a module which defines methods of the
same name as existing class methods, by ensuring that optional methods
are only ever used if the class doesn’t have a method of that name
defined or inherited.

The other advantage was introspection: Interface.needs returns a list of
required methods, Interface.class_needs returns a list of required
singleton methods and Interface.optional returns a list of methods
which the Interface can provide unless the class has it’s own
implementation.

TTFN,

Geoff.

···

On Wed, Nov 19, 2003 at 11:00:43AM +0900, Christoph wrote:

Geoff Youngs wrote:

So, for the above example, using Interface::Enumerable could
allow an array class to use the brute force Enumerable#max,
but wouldn’t prevent the Range class from implementing a much
simpler and faster Range#max.

Whats the improvement here. We can already fine tune
included method for each ``includy’’

class Range
def max
unless exclude_end?
self.end if self.begin <= self.end
else
# the super refers to Enumerable#max
super if self.begin <= self.end
end
end
end

but maybe I missunderstood something(s)?

All I ever really care about is knowing whether or not an object
implements a certain interface, and that interface can either be
described as a mix-in module or an ancestor class. It doesn’t
really matter to me where that interface was defined, any class or
module will do; the object could also be much more evolved than I
expect it to be, and could even have lots of methods overridden. I
don’t really care. All I care about is if it is at least
implementing a certain basic interface.

Ancestry/name checking doesn’t tell you that a method implements a
certain interface. It says that it inherits from a class or includes
a module. You can assume that a name indicates an interface, but
that’s a bad assumption:

class File
class << self
undef :read
end
end

Oops! Now you’ve got something that’s a File, but doesn’t correspond
to the required interface. Granted, this is a pathological case, but
I could see this sort of thing being done in a sandbox situation.

But if something derives or mixes in an interface, then it does implement
the interface. The module or class is the implementation. Sure, you can
hack it out; that can happen in any language. I can pass pointers to int
where a FILE pointer is expected in C and C++. That’s not the point.

Here, watch this pseudo-C++ (or maybe it’s Java to some):

class MyFile : public File
{
int open(const char* filename)
{
throw “this implementation cannot open files”;
}
}

I can create an object of the above class and pass it to functions that expect
an object of class File. If any of those functions uses the open function,
KA-BOOM.

Anyone can deliberately circumvent an interface. That’s not the point. The
point is, there’s a STRONG INDICATION that MyFile implements the File
interface.

It’s a declaration, not a guarantee.

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.

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, 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.

These things are important. It saves a lot of code and headaches when you can
just ask “does this object implement this interface?” You might not find it
useful, but others definitely do. This is one of the things I think Ruby
could do very well and still retain its flexibility. 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.

So, although YOU might not need this, I and others do. 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.

Sean O'Dell
···

On Tuesday 18 November 2003 10:30 pm, Austin Ziegler wrote:

On Wed, 19 Nov 2003 08:08:25 +0900, Sean O’Dell wrote:

Hi –

But if something derives or mixes in an interface, then it does implement
the interface. The module or class is the implementation. Sure, you can
hack it out; that can happen in any language. I can pass pointers to int
where a FILE pointer is expected in C and C++. That’s not the point.

This is similar to Ryan Pavlik’s comments to the effect of “Sure, if
you want to write broken code you can change an object’s
interface…” :slight_smile: The thing is, though, it’s not about hackery or
brokenness; it’s about a language design which accomodates dynamic
type (interface) change, and in which such behavior is not
incompatible with the achievement of good, well-designed code.

David

···

On Thu, 20 Nov 2003, Sean O’Dell wrote:


David A. Black
dblack@wobblini.net

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

···

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:

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

But you can’t dynamically re-implement a method and change the parameters it
takes and then pass an object of that class to a method that doesn’t know
about the new parameters.

Sooner or later, you have to fix the interface in stone and the flexibility is
out the window. It just is. Even if Ruby doesn’t have a mechanism to
enforce type checking, you can’t pass objects that have been modified
radically to libraries that expect a former version to be present. If you
alter parameters, you’re going to get hit with a semi-descriptive error. You
will be forced to either abandon the call, or modify your object to conform.
Either way, the enforcement occurs, regardless of whether Ruby helps you or
not. The dynamicism is gone.

Listen, I don’t mean to be obtuse, but declaring Ruby’s flexibility is not a
guarantee of flexibility. It’s flexible until a library requires certain
methods and parameters. Then you’re stuck. It’s better to get a nice,
descriptive message like “your object does not conform to the :memorymap
interface” than to get a message that says “in line 45,289 of a file you know
nothing about in the middle of a bunch of code you didn’t write, a call was
made to some object whose name you have to trace to find out where it came
from and that call didn’t have the right number of parameters. Figure it out
for yourself, bubba.”

See what I’m getting at? Your flexibility is already gone at this point. I’m
not advocating taking away any more. I’m just advocating a mechanism for
reporting these incompatibilities, so when you are hit with this problem, you
can very quickly assess what went wrong and make an adjustment.

Sean O'Dell
···

On Wednesday 19 November 2003 10:33 am, David A. Black wrote:

On Thu, 20 Nov 2003, Sean O’Dell wrote:

But if something derives or mixes in an interface, then it does
implement the interface. The module or class is the implementation.
Sure, you can hack it out; that can happen in any language. I can pass
pointers to int where a FILE pointer is expected in C and C++. That’s
not the point.

This is similar to Ryan Pavlik’s comments to the effect of “Sure, if
you want to write broken code you can change an object’s
interface…” :slight_smile: The thing is, though, it’s not about hackery or
brokenness; it’s about a language design which accomodates dynamic
type (interface) change, and in which such behavior is not
incompatible with the achievement of good, well-designed code.

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

I do. I almost didn’t choose Ruby because of the lack of type checking. I am
primarily a C++ programmer. My guess is you’re not hearing the argument of
scores of C++/Java programmers because they’re not here, because Ruby has
zero type-checking. I mean that in all seriousness. People don’t fiddle
around arguing with others, generally, they just walk away. I have never
said a word to anyone in the Python group about what could make Python better
because there’s too much wrong with it to begin with. That’s my opinion of
Ruby right now. I love it, but I think there are things like lack of type
checking that are making people go “ah screw it” and just move on.

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:

Then you’re not thinking hard enough about it. I can’t force the notion that
type checking is useful if you really don’t want to listen.

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.

No, documentation is not sufficient, especially since there is so much code
out there that is NOT documented. It would be much easier to provide a
typing mechanism that could at least report which interfaces an object
provides. Developers are more likely to take advantage of that than they are
to provide documentation.

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”.

Well, clearly the hierarchy is not designed to report properly which interface
StringIO implements, which is sort of my point. If there were an abstract IO
interface, StringIO could declare itself an implementation of that interface,
and methods that required that interface would then have very informative
error messages for developers who passed it an non-conforming object.

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.

That is my point exactly. The assumption is already there. The flexibility
is already lost. You MUST provide a certain object which provides a certain
interface, or you end up with a deeply embedded error, instead of an
informative one pointing to your own code.

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

Mostly people new to Ruby recently from Java. Seriously.

No. People experienced in both. I’ve programmed for Ruby for a year or so
now and I still wish for some type checking now and then, when I’m digging
around in someone else’s code trying to figure out what parameters they
expected.

I don’t take being called a C++ or Java programmer as an insult. I take it to
mean that you don’t, or haven’t, programmed in either enough to see type
checking as a blessing, not a curse.

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.

Terminology issue. Okay, let’s call my promotion "interface declaration."
That’s all I advocate right now.

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.

Ah, ancestry checking. I see where we are crossed up in this. Let me be
clearer. I only advocate “interface checking.” A class is just one form of
interface. A module is another, when it’s mixed-in. I wholly support
something like this:

class MyMockRuwikiClass
interface RuwikiClass
end

…where RuwikiClass is the actual class that Ruwiki uses. My mixing it in as
an “interface” your class is more or less saying “I implement the Ruwiki
interface” but doesn’t necessarily need to implement it, and certainly won’t
inherit anything from RuwikiClass. It’s just a declaration. So if the
Ruwiki tokenizer required an object that implements the RuwikiClass
interface, your mock object would do just fine.

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’ve made a pretty good analogy in another post.

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.

Wait, you don’t throw the baby out with the bath water. Ruby is infinitely
superior to PHP. It doesn’t have to become like PHP just to get “interface
declarations.”

Sean O'Dell
···

On Wednesday 19 November 2003 12:04 pm, Austin Ziegler wrote:

On Thu, 20 Nov 2003 02:43:31 +0900, Sean O’Dell wrote:

Hi,

Listen, I don’t mean to be obtuse, but declaring Ruby’s flexibility is not a
guarantee of flexibility. It’s flexible until a library requires certain
methods and parameters. Then you’re stuck. It’s better to get a nice,
descriptive message like “your object does not conform to the :memorymap
interface” than to get a message that says “in line 45,289 of a file you know
nothing about in the middle of a bunch of code you didn’t write, a call was
made to some object whose name you have to trace to find out where it came
from and that call didn’t have the right number of parameters. Figure it out
for yourself, bubba.”

I don’t mean “type checking is bad”, but “type checking based on class
inheritance hierarchy is bad”, and we don’t have any other better type
checking schema in Ruby (yet).

See what I’m getting at? Your flexibility is already gone at this point. I’m
not advocating taking away any more. I’m just advocating a mechanism for
reporting these incompatibilities, so when you are hit with this problem, you
can very quickly assess what went wrong and make an adjustment.

It’s good to have the mechanism like that, I agree, even I put the
feature in my latest slides. But there’s still long way to design and
implement it efficiently, if I am right.

						matz.
···

In message “Re: “stereotyping” (was: Re: Strong Typing (Re: Managing metadata about attribute types) )” on 03/11/20, “Sean O’Dell” sean@celsoft.com writes:

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
I do. I almost didn’t choose Ruby because of the lack of type checking.
I am primarily a C++ programmer. My guess is you’re not hearing the
argument of scores of C++/Java programmers because they’re not here,
because Ruby has zero type-checking.

Likely not. Because if you’re looking at choosing a scripting
language based on its type-checking, you’re excluding the vast
majority of modern scripting languages including Perl, Python,
JavaScript, and Ruby.

I mean that in all seriousness.

I grant that you mean that in all seriousness. But in doing so,
you’re not wanting Ruby. And I mean that in all seriousness.
Interface publication and type checking are two different things.

People don’t fiddle around arguing with others, generally, they
just walk away. I have never said a word to anyone in the Python
group about what could make Python better because there’s too much
wrong with it to begin with. That’s my opinion of Ruby right now.
I love it, but I think there are things like lack of type checking
that are making people go “ah screw it” and just move on.

Again: consider the words of Bruce Eckels. The man knows what he’s
talking about and is amazed by the power of not statically type
checking. It’s saved him loads of work. You want to add the work
back into Ruby development. Optionally, I grant, but part of what
a language designer like Matz must consider, is what changes like
you’re talking about would mean to the “culture” of the language. If
people start using an “optional” method signature mechanism for type
name enforcement, then that changes the assumed and actual
dynamicity of Ruby. I suspect that the number of people thinking “ah
screw it” are few and far between and instead are feeling liberated
from having to specify types unnecessarily.

Consider generics in Java (templates in C++). These effectively
introduce the sort of dynamicity that is found in Perl, Python, and
Ruby. Instead of having to do:

int max(int x, int y) { return x >= y ? x : y }
float max(float x, float y) { return x >= y ? x : y }
double max(double x, double y) { return x >= y ? x : y }

You can do:

def max(x, y); x >= y ? x : y; end

I know which I would prefer, and C++'s move toward generics is a
halting step toward that step.

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:
Then you’re not thinking hard enough about it. I can’t force the
notion that type checking is useful if you really don’t want to
listen.

It’s not that I don’t want to listen: it’s that type checking
doesn’t help in the real world. Ruby is used in a number of real
world large-scale projects.

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.
No, documentation is not sufficient, especially since there is so
much code out there that is NOT documented. It would be much
easier to provide a typing mechanism that could at least report
which interfaces an object provides. Developers are more likely to
take advantage of that than they are to provide documentation.

shrug If you want to think that static type checking is really
going to help, that’s your call. But it won’t.

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".
Well, clearly the hierarchy is not designed to report properly
which interface StringIO implements, which is sort of my point. If
there were an abstract IO interface, StringIO could declare itself
an implementation of that interface, and methods that required
that interface would then have very informative error messages for
developers who passed it an non- conforming object.

Useless runtime processing.

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.
That is my point exactly. The assumption is already there. The
flexibility is already lost. You MUST provide a certain object
which provides a certain interface, or you end up with a deeply
embedded error, instead of an informative one pointing to your own
code.

Incorrect. You might want to look at various libraries to see how
they handle this stuff. Some do it through custom exceptions. Some
do it early (a la Text::Format#hyphenator=).

You might not find it useful, but others definitely do.
Mostly people new to Ruby recently from Java. Seriously.
No. People experienced in both. I’ve programmed for Ruby for a
year or so now and I still wish for some type checking now and
then, when I’m digging around in someone else’s code trying to
figure out what parameters they expected.

I tend to open the code and look at it.

I don’t take being called a C++ or Java programmer as an insult. I
take it to mean that you don’t, or haven’t, programmed in either
enough to see type checking as a blessing, not a curse.

Obviously, you’re not speaking about that which you know. I
recommend against it. I’ve done plenty of C, C++, Java, and Pascal
programming – as well as truly strongly typed languages (Ada,
PL/SQL). Ada provides type checking that is truly useful (and
exceedingly annoying because of its inflexibility), not the
half-assed crap you get in C and C++. I’ve had to write two to
twenty times the amount of code in C/C++, Java, or Pascal to do the
same amount of work in Ruby. Not only that, my Ruby handles more
types than my equivalent statically typed code.

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.
Terminology issue. Okay, let’s call my promotion “interface
declaration.” That’s all I advocate right now.

Still don’t like it. I’d accept method signature publication that
doesn’t actually do anything except provide metadata. Anything that
does anything automatic is too high a cost on runtime and
flexibility. You’re also advocating changing the way that Ruby’s
object hierarchy works. In theory, because Rubyists don’t generally
do ancestry/name checking, this won’t break anything.

You realise, of course, that you could solve this outside of Ruby’s
core entirely:

class Foo
implements IO
end

Foo.new.implements?(IO) # => true

I’ll leave the implementation as an exercise for the reader. Done
right, you could easily add it to all of the core classes, too.
Don’t be surprised, though, if few people use it – like few people
use StrongTyping.

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.
Ah, ancestry checking. I see where we are crossed up in this. Let
me be clearer. I only advocate “interface checking.” A class is
just one form of interface. A module is another, when it’s
mixed-in. I wholly support something like this:

class MyMockRuwikiClass interface RuwikiClass
end

Sorry, I don’t want Java. We’ve already got it and I prefer not to
use it because it requires entirely too much typing to do what
should be simple things (e.g., iterating through lists) or thinking
to do what shouldn’t be that hard (e.g., lists of heterogeneous
items). What’s the difference between “interface Ruwiki” and
"include Ruwiki" (neither of which would work, because Ruwiki isn’t
a module and is therefore unable to be included into other classes).
In fact, Chad was able to determine that there were four methods
needed. If he had attempted to include Ruwiki in his mock class, he
would have gotten tons more functionality.

Remember: the functionality had not yet been abstracted. Because I
didn’t do “interface” checking, Chad was able to Do The Smart Thing
without depending on me having Done The Right Thing to begin with.

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’ve made a pretty good analogy in another post.

If it’s the post I’m thinking of, it doesn’t stand up to the real
world.

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.
Wait, you don’t throw the baby out with the bath water. Ruby is
infinitely superior to PHP. It doesn’t have to become like PHP
just to get “interface declarations.”

I disagree. Ruby plus static typing – or interface checking, if you
want to call it that – isn’t a language that I’m interested in.
(And I must repeat that “interface checking” is still too kind for
it; the reality is that any sort of name checking is a promise
that’s worth less than “the check’s in the mail.”)

-austin

···

On Thu, 20 Nov 2003 05:48:37 +0900, Sean O’Dell wrote:

On Wednesday 19 November 2003 12:04 pm, Austin Ziegler wrote:

On Thu, 20 Nov 2003 02:43:31 +0900, Sean O’Dell wrote:

austin ziegler * austin@halostatue.ca * Toronto, ON, Canada
software designer * pragmatic programmer * 2003.11.19
* 16.45.48

Hi,

Listen, I don’t mean to be obtuse, but declaring Ruby’s flexibility is not
a guarantee of flexibility. It’s flexible until a library requires
certain methods and parameters. Then you’re stuck. It’s better to get a
nice, descriptive message like “your object does not conform to the
:memorymap interface” than to get a message that says “in line 45,289 of
a file you know nothing about in the middle of a bunch of code you didn’t
write, a call was made to some object whose name you have to trace to
find out where it came from and that call didn’t have the right number of
parameters. Figure it out for yourself, bubba.”

I don’t mean “type checking is bad”, but “type checking based on class
inheritance hierarchy is bad”, and we don’t have any other better type
checking schema in Ruby (yet).

No, type checking based on class inheritance (and mix-ins) is completely fine.
Just think of the checking not as “type enforcement” but as “interface
declaration.”

A class is a declaration which says “I do this.” The class can change, and it
can be extended, but it’s just the statement that matters. The programmers
just needs to know. No language can do anything with an object by itself,
only the programmer decides why an object must be of a certain type. Just
having a mechanism to enforce that an object has a certain class in its
hierarchy says “I do this.” So when a programmer makes a mistake and passes
an object that can’t be used by a method, they get an error message that says
"your object does not do what I want."

See what I’m getting at? Your flexibility is already gone at this point.
I’m not advocating taking away any more. I’m just advocating a mechanism
for reporting these incompatibilities, so when you are hit with this
problem, you can very quickly assess what went wrong and make an
adjustment.

It’s good to have the mechanism like that, I agree, even I put the
feature in my latest slides. But there’s still long way to design and
implement it efficiently, if I am right.

Take the easy road. Just go with an “interface declaration” mechanism. Just
enforce that objects are descended from certain classes, or were mixed-in.
If a programmer goes out of his way to change the object radically, fine, it
will break just like it breaks now. No harm done. The alternative would be
to enforce that all expected methods and parameters are present in a precise
form, and that would be insanely strict.

It’s a very open-ended solution. Some people won’t use it at all because they
don’t have any need for typing. Those people will be unaffected. Some
people will need it occassionally, probably library developers mostly. Those
people will have a good way to warn developers when they are passing in the
wrong objects to the library. Sometimes objects will change radically, and
they won’t conform to the interface they declare themselves to conform to.
Oh well. That’s how Ruby is now. The error message will come from deep
inside the library and will be ugly. Oh well. Too bad for the developer who
overrode the method and changed the order/number of parameters. My advice
there is: don’t do that.

Sean O'Dell
···

On Wednesday 19 November 2003 11:14 am, Yukihiro Matsumoto wrote:

In message “Re: “stereotyping” (was: Re: Strong Typing (Re: Managing > metadata about attribute types) )” > > on 03/11/20, “Sean O’Dell” sean@celsoft.com writes:

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

I do. I almost didn’t choose Ruby because of the lack of type checking.
I am primarily a C++ programmer. My guess is you’re not hearing the
argument of scores of C++/Java programmers because they’re not here,
because Ruby has zero type-checking.

Likely not. Because if you’re looking at choosing a scripting
language based on its type-checking, you’re excluding the vast
majority of modern scripting languages including Perl, Python,
JavaScript, and Ruby.

Exactly my point. People are sticking with C++ where they could really
benefit from moving to a script language like Ruby, or they’re learning Java
and they’re stuck being C++/Java programmers.

I mean that in all seriousness.

I grant that you mean that in all seriousness. But in doing so,
you’re not wanting Ruby. And I mean that in all seriousness.
Interface publication and type checking are two different things.

Not really. Interface declaration, and type checking in an OO environment
performed the exact same task: telling code what an object can do.

People don’t fiddle around arguing with others, generally, they
just walk away. I have never said a word to anyone in the Python
group about what could make Python better because there’s too much
wrong with it to begin with. That’s my opinion of Ruby right now.
I love it, but I think there are things like lack of type checking
that are making people go “ah screw it” and just move on.

Again: consider the words of Bruce Eckels. The man knows what he’s
talking about and is amazed by the power of not statically type
checking. It’s saved him loads of work. You want to add the work
back into Ruby development. Optionally, I grant, but part of what
a language designer like Matz must consider, is what changes like
you’re talking about would mean to the “culture” of the language. If
people start using an “optional” method signature mechanism for type
name enforcement, then that changes the assumed and actual
dynamicity of Ruby. I suspect that the number of people thinking “ah
screw it” are few and far between and instead are feeling liberated
from having to specify types unnecessarily.

The programmers who jump into Ruby, yes. Their managers, no. There are
requirements in the real world that go beyond “what feels good” and some of
it has a basis in reality. Being able to ask what interface an object
implements does a lot to make up for bad documentation and crazy error
messages. On large, multi-developer projects, and with poorly documented
libraries, this gets proven every single day. Type checking, in whatever
form it takes, helps a LOT.

Consider generics in Java (templates in C++). These effectively
introduce the sort of dynamicity that is found in Perl, Python, and
Ruby. Instead of having to do:

int max(int x, int y) { return x >= y ? x : y }
float max(float x, float y) { return x >= y ? x : y }
double max(double x, double y) { return x >= y ? x : y }

You can do:

def max(x, y); x >= y ? x : y; end

I know which I would prefer, and C++'s move toward generics is a
halting step toward that step.

I loath templates. But I know what you mean.

I am not an advocate for strong static typing. I only advocate a reasonable
assurance that an object implements a certain interface.

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:

Then you’re not thinking hard enough about it. I can’t force the
notion that type checking is useful if you really don’t want to
listen.

It’s not that I don’t want to listen: it’s that type checking
doesn’t help in the real world. Ruby is used in a number of real
world large-scale projects.

No, it is not. Perhaps one day. But not right now. Java and C++ own that
distinction, and I think if called the conglomeration of scripts written in
Perl that I’ve seen thrown together in one directory a “project” then I think
Perl is a runner-up. Perhaps if you could all the interface code that PHP
has generated, it would also be up there somewhere. Ruby is used in some
fair-sized projects, but I can’t think of a single large, successful one
right now. Python has some doozies, though.

I really think doing some form of intelligent, flexible interface checking
will put Ruby a notch up in a lot of C++/Java developer’s eyes. It won’t be
static type checking, but you can bill it as a suitable replacement, and I’m
sure that’d be good enough for most water-cooler discussions.

No, documentation is not sufficient, especially since there is so
much code out there that is NOT documented. It would be much
easier to provide a typing mechanism that could at least report
which interfaces an object provides. Developers are more likely to
take advantage of that than they are to provide documentation.

shrug If you want to think that static type checking is really
going to help, that’s your call. But it won’t.

Listen. I have said several times that static type checking isn’t what I
want.

Well, clearly the hierarchy is not designed to report properly
which interface StringIO implements, which is sort of my point. If
there were an abstract IO interface, StringIO could declare itself
an implementation of that interface, and methods that required
that interface would then have very informative error messages for
developers who passed it an non- conforming object.

Useless runtime processing.

Listen. My thoughts on this were already written. I said it could be shut
off at runtime.

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.

That is my point exactly. The assumption is already there. The
flexibility is already lost. You MUST provide a certain object
which provides a certain interface, or you end up with a deeply
embedded error, instead of an informative one pointing to your own
code.

Incorrect. You might want to look at various libraries to see how
they handle this stuff. Some do it through custom exceptions. Some
do it early (a la Text::Format#hyphenator=).

That gets tedious, and it’s not something I would be proud to heft onto the
shoulders of all library developers.

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

Mostly people new to Ruby recently from Java. Seriously.

No. People experienced in both. I’ve programmed for Ruby for a
year or so now and I still wish for some type checking now and
then, when I’m digging around in someone else’s code trying to
figure out what parameters they expected.

I tend to open the code and look at it.

That’s you. Don’t make the assumption that performing such a task is fine for
everyone. That’s not a language feature, that’s a personal habit.

I don’t take being called a C++ or Java programmer as an insult. I
take it to mean that you don’t, or haven’t, programmed in either
enough to see type checking as a blessing, not a curse.

Obviously, you’re not speaking about that which you know. I
recommend against it. I’ve done plenty of C, C++, Java, and Pascal
programming – as well as truly strongly typed languages (Ada,
PL/SQL). Ada provides type checking that is truly useful (and
exceedingly annoying because of its inflexibility), not the
half-assed crap you get in C and C++. I’ve had to write two to
twenty times the amount of code in C/C++, Java, or Pascal to do the
same amount of work in Ruby. Not only that, my Ruby handles more
types than my equivalent statically typed code.

You know what Ziegler? If you toss around insults, I’m simply going to ignore
you. Telling a man who’s programmed for 20 years, the last 12 of which were
in C++ that he doesn’t know about C++ is extremely presumptuos and insulting.
If you want to start talking to air, keep insulting me.

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.

Terminology issue. Okay, let’s call my promotion “interface
declaration.” That’s all I advocate right now.

Still don’t like it. I’d accept method signature publication that

Fact is, it doesn’t seem like you like anything I say, so I think my thread
with you is at an impasse.

Sean O'Dell
···

On Wednesday 19 November 2003 03:00 pm, Austin Ziegler wrote:

On Thu, 20 Nov 2003 05:48:37 +0900, Sean O’Dell wrote:

On Wednesday 19 November 2003 12:04 pm, Austin Ziegler wrote:

On Thu, 20 Nov 2003 02:43:31 +0900, Sean O’Dell wrote: