Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

the way class variables work

0 views
Skip to first unread message

David Alan Black

unread,
Aug 10, 2001, 6:09:10 PM8/10/01
to
Hello --

I've been puzzling over Ruby's treatment of class variables -- not so
much the "what" as the "why".

It seems strange to me that a class somewhere off in one side of an
inheritance tree can affect a class variable all over the tree.

For illustration purposes (no claims of greatness of design):

class Vehicle
@@tires = 0
def tire_count
@@tires
end
end

class Car < Vehicle
@@tires = 4
end

class Bike < Vehicle
@@tires = 2
end

puts Car.new.tire_count # => 2


Class variables seem to be not so much per class as per class
hierarchy or family. I can't quite figure out why they work this way,
rather than working like instance methods (so that redefinition in a
child class stops the search process up the inheritance path).

Insights, opinions, and/or words of wisdom welcome.


David

--
David Alan Black
home: dbl...@candle.superlink.net
work: blac...@shu.edu
Web: http://pirate.shu.edu/~blackdav


Tobias Reif

unread,
Aug 10, 2001, 6:33:12 PM8/10/01
to
> class Vehicle
> @@tires = 0
> def tire_count
> @@tires
> end
> end
>
> class Car < Vehicle
> @@tires = 4
> end
>
> class Bike < Vehicle
> @@tires = 2
> end
>
> puts Car.new.tire_count # => 2

This is weird! I really would appreciate enlightening explanations

Tobi

--
Tobias Reif
http://www.pinkjuice.com/myDigitalProfile.xhtml

go_to('www.ruby-lang.org').get(ruby).play.create.have_fun


Jim Menard

unread,
Aug 10, 2001, 8:46:26 PM8/10/01
to
David Alan Black <dbl...@candle.superlink.net> writes:

> Hello --
>
> I've been puzzling over Ruby's treatment of class variables -- not so
> much the "what" as the "why".
>
> It seems strange to me that a class somewhere off in one side of an
> inheritance tree can affect a class variable all over the tree.
>
> For illustration purposes (no claims of greatness of design):
>
> class Vehicle
> @@tires = 0
> def tire_count
> @@tires
> end
> end
>
> class Car < Vehicle
> @@tires = 4
> end
>
> class Bike < Vehicle
> @@tires = 2
> end
>
> puts Car.new.tire_count # => 2
>
>
> Class variables seem to be not so much per class as per class
> hierarchy or family. I can't quite figure out why they work this way,
> rather than working like instance methods (so that redefinition in a
> child class stops the search process up the inheritance path).

Let me try. The key is that classes are objects too, and they have their
own inheritance tree, complete with methods and instance variables
(attributes).

Vehicle ----------- Vehicle class (with attr @@tires)
| \ | \
Car Bike -------- Car class Bike class

In this case, the object that represents the Vehicle class has an instance
variable called 'tires'. That instance variable exists in the Vehicle class
object.

The line "@@tires = 0" creates a new instance variable inside the Vehicle
class object. The lines "@@tires = 4" and "@@tiers = 2" assign new values
to that instance variable, they don't declare new instances.

Jim
--
Jim Menard, ji...@io.com, http://www.io.com/~jimm/
"Don't eat crackers in the bed of your future or you'll get ... scratchy!"
-- The Tick

Nat Pryce

unread,
Aug 10, 2001, 9:04:10 PM8/10/01
to
From: "David Alan Black" <dbl...@candle.superlink.net>

> I've been puzzling over Ruby's treatment of class variables -- not so
> much the "what" as the "why".
>
> It seems strange to me that a class somewhere off in one side of an
> inheritance tree can affect a class variable all over the tree.
>
> For illustration purposes (no claims of greatness of design):
>
> class Vehicle
> @@tires = 0
> def tire_count
> @@tires
> end
> end
>
> class Car < Vehicle
> @@tires = 4
> end
>
> class Bike < Vehicle
> @@tires = 2
> end
>
> puts Car.new.tire_count # => 2
>
>
> Class variables seem to be not so much per class as per class
> hierarchy or family. I can't quite figure out why they work this way,
> rather than working like instance methods (so that redefinition in a
> child class stops the search process up the inheritance path).
>
> Insights, opinions, and/or words of wisdom welcome.

This makes sense to me. @@tyres is defined as a class variable of Vehicle.
Any class derived from Vehicle has access to its instance and class
variables, as per usual scoping rules. Creating the Car and Bike classes
overwrite the value of the @@tyres variable in Vehicle, rather than create a
new variable named @@tyres, because @@tyres is brought into scope by the "<
Vehicle" declaration.

So, yes it's confusing that Car and Bike don't create new class variables.
But conversely it's not confusing that class variables follow the same
scoping rules as instance variables. It's pretty even either way.

I consider this a result of Ruby not requiring variable declarations. E.g.
if a variable 'x' is defined in a scope, then it is defined in any enclosed
scopes, even though those enclosed scopes might want to create a new
variable of that name. There is no way for scopes to declare that they want
a *new* variable rather than use the existing variable.

Chris Uzdavinis

unread,
Aug 10, 2001, 10:36:57 PM8/10/01
to
David Alan Black <dbl...@candle.superlink.net> writes:

> Hello --
>
> I've been puzzling over Ruby's treatment of class variables -- not so
> much the "what" as the "why".

You bring up some good questions, which leads to only more questions.

What does it really mean to be an instance of a class when methods can
be dynamically created? I can have 10 actual instances of Car
(created with Car.new) and they can have absolutely nothing in
common. (Thanks to dynamic addition/removal of interface features,
etc.)

Given a class, there is no guarnatee that any objects instantiated
from it still share its interface at all. It's wishful thinking to
assume two objects with the same "base class" have anything in common
whatsoever, aside from that base class.

Question: Does inheritance honestly have any significant meaning in
Ruby?

I understand how it searches for names, how inheritance CAN be used,
and how all the memory is layed out. I understand the similarities
that most instances of classes shares. But I also understand that
this is an absolutely false claim in Ruby:

"x1 and x2 were both created from class X, therefore x1 and x2
have the same type."

I'm not knocking Ruby, I *really* it, but I think that its incredible
dynamic power has necessarily caused the impotence of other
traditional OO concepts. Nothing comes without a cost.

Traditional "is-a" object oriented theory does not apply to Ruby.
Ruby is about interfaces, not about types, not about hierarchies.

It's not bad, it is just different.

--
Chris

David Alan Black

unread,
Aug 10, 2001, 10:54:28 PM8/10/01
to
Hello --

On Sat, 11 Aug 2001, Jim Menard wrote:

> David Alan Black <dbl...@candle.superlink.net> writes:
>
> > Hello --
> >
> > I've been puzzling over Ruby's treatment of class variables -- not so
> > much the "what" as the "why".
> >
> > It seems strange to me that a class somewhere off in one side of an
> > inheritance tree can affect a class variable all over the tree.

[...]

> > Class variables seem to be not so much per class as per class
> > hierarchy or family. I can't quite figure out why they work this way,
> > rather than working like instance methods (so that redefinition in a
> > child class stops the search process up the inheritance path).
>
> Let me try. The key is that classes are objects too, and they have their
> own inheritance tree, complete with methods and instance variables
> (attributes).
>
> Vehicle ----------- Vehicle class (with attr @@tires)
> | \ | \
> Car Bike -------- Car class Bike class
>
> In this case, the object that represents the Vehicle class has an instance
> variable called 'tires'. That instance variable exists in the Vehicle class
> object.
>
> The line "@@tires = 0" creates a new instance variable inside the Vehicle
> class object. The lines "@@tires = 4" and "@@tiers = 2" assign new values
> to that instance variable, they don't declare new instances.

But creating a new class does create a new instance of class Class.
So if class variables can be understood as instance variables of
instances of class Class, then in this example:

class A
@@cv = 123
end

class B < A
end

where we have two instances of class Class, each instance should (in
my opinion) have its own copies of its instance variables, and
changing B's @@cv should not affect A's @@cv. That would conform to
how instance variables work elsewhere.

Even though class B is a child of class A, it should have as much
"right" to its own instance variables as class A has. A and B are
both, equally, instances of class Class. Their parent/child
relationship is irrelevant to this.

That relationship *is* relevant to how things are searched for.
OK... so in the cases of class methods, instance methods, constants,
and embedded class and module definitions, the way it works is: the
child class can override or redefine the thing, but if it doesn't, the
parent class's version (if any) will be used.

Class variables work completely differently. The child class *cannot*
say, "Thank you very much, Parent, but I'm going to do my own version
of that." That's why I find class variables anomalous.

Also, can it really be good, from the point of view of code
maintenance, for a child class to have the power to change its parent,
and all of that parent's other children, including future ones?
Doesn't that make things very unstable?

MikkelFJ

unread,
Aug 11, 2001, 5:36:56 AM8/11/01
to

"David Alan Black" <dbl...@candle.superlink.net> wrote in message
news:Pine.LNX.4.30.01081...@candle.superlink.net...

> > Let me try. The key is that classes are objects too, and they have their

Initially, this was also my thought.
...

> But creating a new class does create a new instance of class Class.

Yes, you have a point there.

> Also, can it really be good, from the point of view of code
> maintenance, for a child class to have the power to change its parent,
> and all of that parent's other children, including future ones?
> Doesn't that make things very unstable?

I would seem so.

On the other hand, I wasn't really surprised that your example behaved as it
did - whether it is illogical or not. And Ruby has a lot of dynamic power
that can be questioned from a maintainance point of view: You can't possible
know what an object is, because some obscure corner of the code could have
changed it.

Therefore, your argument might not imly that the design is wrong.

But maybe classes aren't really objects after all. They are metaobjects, and
inheritance has a different meaning, where the entire family is an object.
It does give me a headache to think about this conceptually, although the
actual use is intuitive.


Mikkel

Tobias Reif

unread,
Aug 11, 2001, 6:06:17 AM8/11/01
to
Hi

It seems as if subclasses just extend their parentclasses, but are no
classes themselves. So class variables get overridden by the last
assignment. This is counterintuitive.
David Black's example shows this, and also creates a problem to be
solved, because it has semantic logic:
(generalizations)
A car is a vehicle with 4 tires.
A Bike is a vehikle with 2 tires.
How to implement these circumstances nicely in Ruby, when all subclasses
share class variables with their parents, and override them
'arbitrarily'? Using def initialize? I'm sure a nice solution is
possible, but the behaviour David Black describes is still disturbing.

Tobi

> > Also, can it really be good, from the point of view of code
> > maintenance, for a child class to have the power to change its parent,
> > and all of that parent's other children, including future ones?
> > Doesn't that make things very unstable?
>
> I would seem so.

--

Tobias Reif

unread,
Aug 11, 2001, 6:49:08 AM8/11/01
to
Here's a statement:
5. para of
http://www.students.stedwards.edu/~cherbig/report.htm
"The scope of class variables is limited to the entire class that
created the variable and to any subclasses. This feature has been
compared to the protected value of Java attributes (Hunt)."

> I've been puzzling over Ruby's treatment of class variables -- not so
> much the "what" as the "why".
>
> It seems strange to me that a class somewhere off in one side of an
> inheritance tree can affect a class variable all over the tree.

David Alan Black

unread,
Aug 11, 2001, 7:12:49 AM8/11/01
to
Hello --

On Sat, 11 Aug 2001, Tobias Reif wrote:

> Hi
>
> It seems as if subclasses just extend their parentclasses, but are no
> classes themselves. So class variables get overridden by the last
> assignment. This is counterintuitive.

That's the thing, though: subclasses *are* classes themselves. If
they weren't, Object would be the only real class :-)

> David Black's example shows this, and also creates a problem to be
> solved, because it has semantic logic:
> (generalizations)
> A car is a vehicle with 4 tires.
> A Bike is a vehikle with 2 tires.
> How to implement these circumstances nicely in Ruby, when all subclasses
> share class variables with their parents, and override them
> 'arbitrarily'? Using def initialize? I'm sure a nice solution is
> possible, but the behaviour David Black describes is still disturbing.

You can definitely do something like this without class variables; for
example:

class Vehicle
def tires
end

def talk
puts "I have #{tires} tires"
end
end

class Car < Vehicle
def tires ; 4 ; end
end

class Bike < Vehicle
def tires ; 2 ; end
end

Car.new.talk # => I have 4 tires


or using constants:

class Vehicle
TIRES = nil
def talk
puts "I have #{type::TIRES} tires"
end
end

class Car < Vehicle
TIRES = 4
end

class Bike < Vehicle
TIRES = 2
end

Car.new.talk

David Alan Black

unread,
Aug 11, 2001, 7:39:29 AM8/11/01
to
Hello --

On Sat, 11 Aug 2001, MikkelFJ wrote:

> On the other hand, I wasn't really surprised that your example behaved as it
> did - whether it is illogical or not. And Ruby has a lot of dynamic power
> that can be questioned from a maintainance point of view: You can't possible
> know what an object is, because some obscure corner of the code could have
> changed it.

That's true, but that power is parceled out in sync with certain
built-in thresholds. For instance, even though we know it's possible
to do this:

class A
def talk ; puts "hi" ; end
end

class B < A
A.class_eval <<-EOE
alias :oldtalk :talk
def talk ; puts "hello" ; end
EOE
end

class C < A
end

C.new.talk # => hello


we would still, I think, not be happy if we started seeing this:

class A
def talk ; puts "hi" ; end
end

class B < A
def talk ; puts "hello" ; end
end

class C < A
end

C.new.talk # => hello


The fact that class variables behave this way may not matter as much
as it would if other things (like instance methods) behaved this way,
but my reaction to it is at the level of feeling that it interferes
with the infrastructure on which Ruby's freedom is built.

> Therefore, your argument might not imly that the design is wrong.
>
> But maybe classes aren't really objects after all. They are metaobjects, and
> inheritance has a different meaning, where the entire family is an object.
> It does give me a headache to think about this conceptually, although the
> actual use is intuitive.

But classes definitely are objects. The entire family might in some
sense be a metaobject, though I'm not sure what lies down that
path :-)

ts

unread,
Aug 11, 2001, 7:43:36 AM8/11/01
to
>>>>> "D" == David Alan Black <dbl...@candle.superlink.net> writes:

D> But creating a new class does create a new instance of class Class.
D> So if class variables can be understood as instance variables of
D> instances of class Class, then in this example:

D> class A
D> @@cv = 123
D> end

D> class B < A
D> end

pigeon% cat b.rb
#!/usr/bin/ruby
class A
@cv = 123
def A.cv
@cv
end
end

class B < A
@cv = 24
def B.cv
@cv
end
end

p A.cv
p B.cv
pigeon%

pigeon% b.rb
123
24
pigeon%


Guy Decoux


David Alan Black

unread,
Aug 11, 2001, 8:27:33 AM8/11/01
to
Hello --

On Sat, 11 Aug 2001, Chris Uzdavinis wrote:

> David Alan Black <dbl...@candle.superlink.net> writes:
>
> > Hello --
> >
> > I've been puzzling over Ruby's treatment of class variables -- not so
> > much the "what" as the "why".
>
> You bring up some good questions, which leads to only more questions.

So do you, and so do they :-)

Quoting back selectively:

> What does it really mean to be an instance of a class when methods can
> be dynamically created?

..


> Given a class, there is no guarnatee that any objects instantiated
> from it still share its interface at all. It's wishful thinking to
> assume two objects with the same "base class" have anything in common
> whatsoever, aside from that base class.

..


> Question: Does inheritance honestly have any significant meaning in
> Ruby?
>

..


> this is an absolutely false claim in Ruby:
>
> "x1 and x2 were both created from class X, therefore x1 and x2

..


> Traditional "is-a" object oriented theory does not apply to Ruby.
> Ruby is about interfaces, not about types, not about hierarchies.

What I find fascinating about this area of reflection and discussion
(of which there's been a good bit lately on irc and elsewhere) is the
question of how it relates to the order within which Ruby's freedom
operates. In fact, if I may meta-ize that last quoted sentence, I see
three ways of looking at Ruby itself:

Way 1: Ruby presents different interfaces to the programmer. You can,
if you choose and/or need to, use Ruby to handle your traditionally OO
projects. Or you can use Ruby pseudo-procedurally. Or you can tap
into Ruby's powers in the area of dynamic method creation and related
things.

Way 2: Ruby is a certain type of programming language.

Way 3: Ruby's range of powers is arranged in a hierarchy. Writing
procedural code in Ruby is infantile. Writing traditional OO
programs, where a Horse "is a" Animal, is adolescent. Breaking with
these things signifies Ruby adulthood.

In my view, neither Way 2 nor Way 3 gets us very far. I tend to find
Way 1 the most meaningful (which meshes nicely, in a meta kind of way,
with your sentence :-).

Maybe it's because I write programs in situations where there really
isn't any chance that someone else is going to add methods to my
objects... but I have never found any reason not to use a traditional
OO style in Ruby, when it suits a project. And when I write
procedural, shell-scriptish things in Ruby, they work. (Which sounds
kind of trivial but is very much at the heart of things.)

It's interesting in this connection that you say, "It's wishful


thinking to assume two objects with the same "base class" have

anything in common whatsoever, aside from that base class." I
completely understand what you mean, in terms of Ruby's programming
facilities. But it has a sort of ominous sound, as if the code might
change itself when your back is turned. I wonder if that's actually
true in some situations :-) And whether, in other situations, it's a
matter of decision-making, rather than wishful thinking.

There is nothing about Ruby that I do not want to understand and
possibly use. I am *not* making some kind of case for pretending that
Ruby is a traditional OO language. I'm just working toward a view
that comprises the various, ummmm, facets of Ruby. (Or interfacets.)

jwei...@one.net

unread,
Aug 11, 2001, 8:49:17 AM8/11/01
to
>>>>> "David" == David Alan Black <dbl...@candle.superlink.net> writes:

David> I've been puzzling over Ruby's treatment of class variables
David> -- not so much the "what" as the "why".

[... example elided ...]

David> Class variables seem to be not so much per class as per
David> class hierarchy or family. I can't quite figure out why
David> they work this way, rather than working like instance
David> methods (so that redefinition in a child class stops the
David> search process up the inheritance path).

David> Insights, opinions, and/or words of wisdom welcome.

In Smalltalk, there are two kinds of "class" variables. There are
class variables which work the way Ruby class variables work (shared
across an inheritance hierarchy). But there are also class *instance*
variables which are unique to the individual class object (instance)
where they are defined.

It turns out that both types of class variables are supported in Ruby
as well...

class A
class << self
def class_instance_var
@class_instance_var
end
def class_var
@@class_var
end
end

@@class_var = 0 # Shared across hierarchy
@class_instance_var = 10 # Unique to class
end

class B < A
@@class_var = 1 # Shared
@class_instance_var = 11 # Unique
end

puts A.class_var # => 1
puts B.class_var # => 1
puts A.class_instance_var # => 10
puts B.class_instance_var # => 11

Class variable mimic "static" variables in Java or C++. Class
instance variables are instance variables in the class object.

I hope this helps.

--
-- Jim Weirich jwei...@one.net http://w3.one.net/~jweirich
---------------------------------------------------------------------
"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)


MikkelFJ

unread,
Aug 11, 2001, 9:43:21 AM8/11/01
to

<jwei...@one.net> wrote in message
news:m266bul...@skaro.access.one.net...

> >>>>> "David" == David Alan Black <dbl...@candle.superlink.net> writes:

> Class variable mimic "static" variables in Java or C++. Class
> instance variables are instance variables in the class object.

I was thinking about the C++ analogy as well. But it doesn't hold.
If you derive a class in C++ , any static members would have to be
re-implemented in the derived class.
The derived class will not share the static member. In templates you don't
have to explicitly declare the instance member implementation, but it still
creates a new instance.
This is an important aspect of buffer-management in container classes -
although it might actually have been better to share the buffer impl.
between family members.

Mikkel

MikkelFJ

unread,
Aug 11, 2001, 9:57:08 AM8/11/01
to

"David Alan Black" <dbl...@candle.superlink.net> wrote in message
news:Pine.LNX.4.30.01081...@candle.superlink.net...

> Way 1: Ruby presents different interfaces to the programmer. You can,


> if you choose and/or need to, use Ruby to handle your traditionally OO
> projects. Or you can use Ruby pseudo-procedurally. Or you can tap
> into Ruby's powers in the area of dynamic method creation and related
> things.

But then we should perhaps go in the other direction:
Add syntax for manipulating class functions in a way that distribute to all
derived classes and object instances. It sure is a mess, but imagine the
interesting things you could do, if used appropriately.
The justification is that you either use OO via the Ruby OO interface and
code doesn't mutate. Or you enter the Ruby mutation interface and think in
an entirely different way about programming.
Objects becomes representations of something larger - like a diplomat or
attache, where instructions can change during the course of action. Name
this Representation Oriented Programming ROP.
For instance you could change trade strategy for a family of stock trading
agents.

This is only a mess if you assume you objects doesn't change. If you start
to think about objects as dynamic individuals in a changing world, you
wouldn't be surprised anymore.

Mikkel

David Alan Black

unread,
Aug 11, 2001, 10:09:37 AM8/11/01
to
Hello --

On Sat, 11 Aug 2001 jwei...@one.net wrote:

> In Smalltalk, there are two kinds of "class" variables. There are
> class variables which work the way Ruby class variables work (shared
> across an inheritance hierarchy). But there are also class *instance*
> variables which are unique to the individual class object (instance)
> where they are defined.
>
> It turns out that both types of class variables are supported in Ruby
> as well...

..

> Class variable mimic "static" variables in Java or C++. Class
> instance variables are instance variables in the class object.
>
> I hope this helps.

Yes, it does, along with some of the other responses and some irc
remarks by matju and others. At least, I'm cured of wanting class
variables to behave like class instance variables, since those already
exist :-)

But I'm still baffled, maybe more than ever, by the "why", that is,
why class variables exist at all in Ruby. No one seems to have
anything good to say about them. They seem to operate as a kind of
not-quite-global global. They introduce weird, backwards
dependencies. They come to your house at night, ring the doorbell,
and run away. They.... [Cut to shot of David cowering in corner]

Tobias Reif

unread,
Aug 11, 2001, 10:16:58 AM8/11/01
to
David Alan Black wrote:
> But I'm still baffled, maybe more than ever, by the "why", that is,
> why class variables exist at all in Ruby. No one seems to have
> anything good to say about them. They seem to operate as a kind of
> not-quite-global global. They introduce weird, backwards
> dependencies. They come to your house at night, ring the doorbell,
> and run away. They.... [Cut to shot of David cowering in corner]

Maaaatz!... heeelp!... where aaare youuuu ...

I really would appreciate enlightening explanations.

Tobi

David Alan Black

unread,
Aug 11, 2001, 10:21:32 AM8/11/01
to
Hello --

On Sat, 11 Aug 2001, MikkelFJ wrote:

>
> "David Alan Black" <dbl...@candle.superlink.net> wrote in message
> news:Pine.LNX.4.30.01081...@candle.superlink.net...
>
> > Way 1: Ruby presents different interfaces to the programmer. You can,
> > if you choose and/or need to, use Ruby to handle your traditionally OO
> > projects. Or you can use Ruby pseudo-procedurally. Or you can tap
> > into Ruby's powers in the area of dynamic method creation and related
> > things.
>
> But then we should perhaps go in the other direction:
> Add syntax for manipulating class functions in a way that distribute to all
> derived classes and object instances. It sure is a mess, but imagine the
> interesting things you could do, if used appropriately.

Isn't that what happens if you manipulate a parent class (without
having overridden the things you're manipulating)?

class A
def talk; puts "original" ; end
end

class B < A ; end

class A
def talk; puts "changed" ; end
end

B.new.talk # => changed

ts

unread,
Aug 11, 2001, 10:20:06 AM8/11/01
to
>>>>> "D" == David Alan Black <dbl...@candle.superlink.net> writes:

D> But I'm still baffled, maybe more than ever, by the "why", that is,
D> why class variables exist at all in Ruby. No one seems to have
D> anything good to say about them. They seem to operate as a kind of
D> not-quite-global global.

This will probably not reply to your question, but initially they were
called "shared variable" (rb_shared_variable_declare)


Guy Decoux


Tobias Reif

unread,
Aug 11, 2001, 10:26:37 AM8/11/01
to
> This will probably not reply to your question, but initially they were
> called "shared variable" (rb_shared_variable_declare)
> Guy Decoux

Maybe they can be called "family variables", because they are shared by
the parent class, all child classes, and their sibling classes.

Nat Pryce

unread,
Aug 11, 2001, 10:35:19 AM8/11/01
to

----- Original Message -----
From: "David Alan Black" <dbl...@candle.superlink.net>
> On Sat, 11 Aug 2001, Chris Uzdavinis wrote:
> > David Alan Black <dbl...@candle.superlink.net> writes:
> > Given a class, there is no guarnatee that any objects instantiated
> > from it still share its interface at all. It's wishful thinking to
> > assume two objects with the same "base class" have anything in common
> > whatsoever, aside from that base class.
> ...
> > Question: Does inheritance honestly have any significant meaning in
> > Ruby?

It depends what you mean by "significant meaning"... :-)

Inheritance has no relationship to type in Ruby (or Smalltalk or other
dynamically
typed OO languages). Type is defined by, and only by, the messages that an
object understands at any given time. Inheritance is merely a convenience
mechanism
for reusing some or all of an implementation defined by another class.
Inherited
implementation can affect the type of objects instantiated from the derived
class, but
that can be overridden at any time.

In Ruby, instead of defining classes or interfaces, you define protocols
that
objects should speak, and define those protocols outside the language.
You can then define useful classes or modules that implement those
protocols,
but a programmer can just as easily write the protocol from scratch.

An simple example of this kind of protocol is the relationship between the
methods hash and eql? or the Enumerable mixin requiring the existence of
the each method.

Cheers,
Nat.

MikkelFJ

unread,
Aug 11, 2001, 11:01:28 AM8/11/01
to

"David Alan Black" <dbl...@candle.superlink.net> wrote in message
news:Pine.LNX.4.30.01081...@candle.superlink.net...

> > But then we should perhaps go in the other direction:
...


> Isn't that what happens if you manipulate a parent class (without
> having overridden the things you're manipulating)?

...
I have never tried, I just assumed that once you had created your class,
changes to parent would not be transparent. But if this is true (as I trust
it is), then there is no difference between ringing doorbells at night using
methods instead of class variables. Then it is just the natural state of
things in Ruby.

Is there a theory for these implications - for example through work on
SmallTalk - or is this new ground where we still need to evolve good models?

Can we find programming problems that Ruby can solve significantly different
from solutions of other languages by taking advantage of this behaviour?

Mikkel

Yukihiro Matsumoto

unread,
Aug 11, 2001, 11:07:02 AM8/11/01
to
Hi,

In message "[ruby-talk:19506] the way class variables work"


on 01/08/11, David Alan Black <dbl...@candle.superlink.net> writes:

| class Vehicle
| @@tires = 0
| def tire_count
| @@tires
| end
| end
|
| class Car < Vehicle
| @@tires = 4
| end
|
| class Bike < Vehicle
| @@tires = 2
| end
|
| puts Car.new.tire_count # => 2

Class variables are to share value among classes/modules and its
instances. If you want to have per class information, you have class
instance variable, or constants.

class instance variables

class Vehicle
@tires = 0
def Vehicle::tire_count
@tires
end
def tire_count
type::tire_count
end
end

class Car < Vehicle
@tires = 4
end

class Bike < Vehicle
@tires = 2
end

puts Car.new.tire_count # => 4

constants

class Vehicle
def tire_count
type::TIRES
end
end

class Car < Vehicle
TIRES = 4
end

class Bike < Vehicle
TIRES = 2
end

puts Car.new.tire_count # => 4

class variables are global variables with limited scope (to specific
class hierarchy), so I do not recommend to abuse them.

matz.


ts

unread,
Aug 11, 2001, 11:13:20 AM8/11/01
to
>>>>> "M" == MikkelFJ <mikkelj-...@post1.dknet.dk> writes:

M> I have never tried, I just assumed that once you had created your class,
M> changes to parent would not be transparent.

There is at least one case where it's not totally transparent. This is not
really for parent but when you use a module, for example :


pigeon% cat b.rb
#!/usr/bin/ruby
module A
def a
puts "a"
end
end

class C
include A
end

C.new.a

module B
def b
puts "b"
end
end

module A
include B
def a
puts "A"
end
end

C.new.a
C.new.b
pigeon%

pigeon% b.rb
a
A
./b.rb:28: undefined method `b' for #<C:0x401a6270> (NameError)
pigeon%


Guy Decoux


jwei...@one.net

unread,
Aug 11, 2001, 12:18:50 PM8/11/01
to
>>>>> "MikkelFJ" == MikkelFJ <mikkelj-...@post1.dknet.dk> writes:

>> Class variable mimic "static" variables in Java or C++. Class
>> instance variables are instance variables in the class object.

MikkelFJ> I was thinking about the C++ analogy as well. But it
MikkelFJ> doesn't hold. If you derive a class in C++ , any static
MikkelFJ> members would have to be re-implemented in the derived
MikkelFJ> class. [...]
MikkelFJ> The derived class will not share the static member.

Actually, it does. You can hide it by declaring another variable in a
more local scope, but the original is still there, and accessible
(with the :: operator).

MikkelFJ> In templates you don't have to explicitly declare the
MikkelFJ> instance member implementation, but it still creates a
MikkelFJ> new instance.

Templates declare a family of classes that are not related through
inheritance. The analogy doesn't carry over.

Comparison to C++ is tricky, because the semantics of the two
languages are subtly different, but I think the parallel holds.
However, if it doesn't help with *your* understanding, then forget it.

MikkelFJ

unread,
Aug 11, 2001, 1:26:55 PM8/11/01
to

"ts" <dec...@moulon.inra.fr> wrote in message
news:200108111511...@moulon.inra.fr...

> >>>>> "M" == MikkelFJ <mikkelj-...@post1.dknet.dk> writes:
>
> M> I have never tried, I just assumed that once you had created your
class,
> M> changes to parent would not be transparent.
>
> There is at least one case where it's not totally transparent. This is
not
> really for parent but when you use a module, for example :

I can understand it by thinking of a parent class as a parent pointer, and
mixins as bulk copy operations.

Ruby is easy to learn, but difficult to master.
And like fire it is a very useful friend, and a very dangerous enemy.


Mikkel

MikkelFJ

unread,
Aug 11, 2001, 1:33:18 PM8/11/01
to
> Actually, it does. You can hide it by declaring another variable in a
> more local scope, but the original is still there, and accessible
> (with the :: operator).

Of course you are right - I've been depending on this fact many times when
specializing container classes. Somehow I still manage to confuse myself.

Mikkel

David Alan Black

unread,
Aug 11, 2001, 1:41:14 PM8/11/01
to
Hello -

On Sun, 12 Aug 2001, MikkelFJ wrote:

>
> "David Alan Black" <dbl...@candle.superlink.net> wrote in message
> news:Pine.LNX.4.30.01081...@candle.superlink.net...
>
> > > But then we should perhaps go in the other direction:

> ....


> > Isn't that what happens if you manipulate a parent class (without
> > having overridden the things you're manipulating)?

> ....


> I have never tried, I just assumed that once you had created your class,
> changes to parent would not be transparent. But if this is true (as I trust
> it is), then there is no difference between ringing doorbells at night using
> methods instead of class variables. Then it is just the natural state of
> things in Ruby.

[Note: I'm not complaining about class variables any more... just
discussing :-]

The class variable model is different from the method model because
any child class can redefine any method, which will (a) shield it from
changes in the parent, and (b) not affect the parent or the siblings.
(See also [ruby-talk:19522].)

Mathieu Bouchard

unread,
Aug 11, 2001, 3:59:02 PM8/11/01
to

On Sat, 11 Aug 2001, David Alan Black wrote:
>
> But I'm still baffled, maybe more than ever, by the "why", that is,
> why class variables exist at all in Ruby. No one seems to have
> anything good to say about them. They seem to operate as a kind of
> not-quite-global global. They introduce weird, backwards
> dependencies. They come to your house at night, ring the doorbell,
> and run away. They.... [Cut to shot of David cowering in corner]
>

It's way easier to explain why "class variables" exist than to explain why
in SmallTalk (a language designed so that kids can use it), class creation
method is called
Class>>subclass:instanceVariableNames:classVariableNames:poolDictionaries:category:.

Consider yourself lucky.

matju


Mathieu Bouchard

unread,
Aug 11, 2001, 4:15:35 PM8/11/01
to

On Sun, 12 Aug 2001, MikkelFJ wrote:
>> There is at least one case where it's not totally transparent. This is
>> not really for parent but when you use a module, for example :
>
> I can understand it by thinking of a parent class as a parent pointer, and
> mixins as bulk copy operations.

mixins work with 'pointers' as well. I think the problem Guy is showing is
that Classes cache method lookup, but Modules don't implement Observable,
so Classes can't subscribe to them, so Classes can't know when Modules
change, and thus can't invalidate their caches.

Or something like that. (but Classes don't implement Observable either,
and Classes are Modules anyway, ...)

I'd call this a bug, but Guy knows more about it than I do.

matju


Phil Tomson

unread,
Aug 11, 2001, 5:03:39 PM8/11/01
to
In article <Pine.LNX.4.30.01081...@candle.superlink.net>,
David Alan Black <dbl...@candle.superlink.net> wrote:
>Hello --

>
>
>But I'm still baffled, maybe more than ever, by the "why", that is,
>why class variables exist at all in Ruby. No one seems to have
>anything good to say about them. They seem to operate as a kind of
>not-quite-global global. They introduce weird, backwards
>dependencies. They come to your house at night, ring the doorbell,
>and run away. They.... [Cut to shot of David cowering in corner]


It is kind of troubling, however, as I thought about it, in practice it
isn't generally an issue.

For example, the 'classic' use of a class variable is to keep track of how
many objects of a certain class exist. In cases like this, the superclass
(or parent class, or whatever you want to call it) always exclusively
manipulates the class variable. For example:

class Vehicle

@@number_of_Vehicles = 0

def initialize
@@number_of_Vehicles += 1 #increment number of vehicles
end

end

class Bicycle < Vehicle

@@number_of_Bycycles = 0

def initialize
super
@@number_of_Bycycles += 1
end
end

class Car < Vehicle

@@number_of_Cars = 0

def initialize
super
@@number_of_Cars += 1
end

end

(actually, I think there are other ways to determine how many objects of a
given class exist in Ruby, so in reality one may not do this)
Of course this is just a convention that one follows, you could still
access @@number_of_Vehicles within Class Car, but you avoid doing so
because "it wouldn't be prudent".

Is there anyway to apply private access control to class variables? (if
not, should there be a way?) Should there be a way to say:

class Vehicle
private @@number_of_Vehicles = 0
public
def initialize
@@number_of_Vehicles += 1
end
end

class Car < Vehicle
def initialize
super
#now try to change @@number_of_Vehicles
@@number_of_Vehicles += 10 #<-- this would generate an access error
end
end

The proposed behavior would be that the line that trys to modify
@@number_of_Vehicles in class Car would generate an access error.
(BTW: this class declaration for Vehicle with the private
@@number_of_Vehicles actually 'compiles' and runs for me without any
problems - but I can still change the value of @@number_of_Vehicles in the
subclass car)

Is it worthwhile to add this kind of behavior?

Phil

Chris Uzdavinis

unread,
Aug 11, 2001, 5:36:11 PM8/11/01
to
David Alan Black <dbl...@candle.superlink.net> writes:

> Hello --


> It's interesting in this connection that you say, "It's wishful
> thinking to assume two objects with the same "base class" have
> anything in common whatsoever, aside from that base class." I
> completely understand what you mean, in terms of Ruby's programming
> facilities. But it has a sort of ominous sound, as if the code might
> change itself when your back is turned. I wonder if that's actually
> true in some situations :-) And whether, in other situations, it's a
> matter of decision-making, rather than wishful thinking.

It could happen, if you take user-supplied input and mix that with an
'eval' statmement, then suddenly you no longer have control of what
your program does. :)

> There is nothing about Ruby that I do not want to understand and
> possibly use. I am *not* making some kind of case for pretending that
> Ruby is a traditional OO language. I'm just working toward a view
> that comprises the various, ummmm, facets of Ruby. (Or interfacets.)

I find it hard to stop thinking in terms of C++ and overriding virtual
functions, etc. But that just dosn't seem to be the way to design
code in ruby, for all the peviously mentioned reasons.

I am still trying to figure out how one can write non-trivial
applications in Ruby, since the interfaces can dynamically change, and
you can't find out until runtime if your code has type-errors.

What I don't know very well is how to recover. Given the dynamic
nature of Ruby, we should be able to calculate the missing types and
add them as necessary and try again. However, normal error-correction
logic usually applies toward values, not types. I don't know if this
kind of computing has been done enough to have a satisfactory general
solution.

Since functions are objects, it seems reasonable to be able to ask a
function object "before I call you, what requirements must my
parameters satisfy?"

I'll give you a function:

def foo(arg)
# stuff
end

Now, how can I call foo such that there is no type error? Let's say
that if there is an error, I can try again with a different argument
that (hopefully) works, as many times as necessary until I get it
"right." Since there can be an infinite number of types (not hard at
all in Ruby!) if I don't have context, it's certainly plausible to
think that brute force would never find the right type to pass into
foo.

It wants something, but won't tell me what it wants. I could have a
conversion error, or a missing interface piece (it does tell me that,
based on the exception type) and the message contains in a string some
information that a programmer can read and figure out. But what seems
to be lacking is information for the program ITSELF to scan the
exception object and have enought enformation to make the proper
changes.


If foo is dynamically created, it's my code doesn't know anything
about it. Assuming I have to call it, if I get a TypeError exception
how do I discover what type I should have provided? I'm not convinced
that there is enough information available to actually infer the type.

But I'm interested to hear what others think.

--
Chris

Chris Uzdavinis

unread,
Aug 11, 2001, 6:40:49 PM8/11/01
to
"Nat Pryce" <nat....@b13media.com> writes:

> It depends what you mean by "significant meaning"... :-)

:)

> Inheritance has no relationship to type in Ruby (or Smalltalk or
> other dynamically typed OO languages). Type is defined by, and only
> by, the messages that an object understands at any given
> time.

Exactly.

> Inheritance is merely a convenience mechanism for reusing some or
> all of an implementation defined by another class. Inherited
> implementation can affect the type of objects instantiated from the
> derived class, but that can be overridden at any time.

Yes, it's a temporal relationship. Just like humans, we inherit genes
from our parents, but we can come out VERY different. Unlike humans,
a ruby object can change its genes.

> In Ruby, instead of defining classes or interfaces, you define
> protocols that objects should speak, and define those protocols
> outside the language.

This is interesting. What distinction do you make between protocol
and interface? Can the protocol be independent from the interface?

> You can then define useful classes or modules that implement those
> protocols, but a programmer can just as easily write the protocol
> from scratch.

Yes, in that regard, every ruby function is like a C++ function
template. (Gotta make those C++ comparisons...!)

template <typename T> void foo(T arg) { /*...*/ }

Rather than an "interface" which is a fixed, hard coded set of
requirments, of which perhaps only a subset is actually used,
templates use what they call the "concept" approach. ANY arg can be
passed in provided that it supports all the necessary concepts which
the code places on it. (The difference is a compile-time check is
made, rather than runtime.)

> An simple example of this kind of protocol is the relationship
> between the methods hash and eql? or the Enumerable mixin requiring
> the existence of the each method.

These are good examples. Do you think that when we write Ruby code
that this type of mindset should be the primary kind of issues we
think about?

How would you suggest we recover when an argument does not support the
protocol? Since Ruby is dynamic, object x may not support it now, but
if you try again later it might. Or vice versa.

There certainly is an element of uncertainty introduced along with all
the dynamic capabilities. The trick is keeping it balanced to where
you can know what's going on but still have flexibility.

I still have this uneasy feeling, though, that nothing can ever be
completely trusted, because perfectly working code can easily be
broken by other code in another file which is completely unrelated to it.

It seems you have to really know everything that is going on in a
program to know if it's going to work. Though there are Modules to
kind of separate things, it's not truly modular since if your code and
mine are running in the same program, my code can break yours, and
vice versa.

The risk to me is not all just type, but behavior too. In a big
application it's unreasonable to expect everyone to study every line
of every file that everyone else is working on.

So when someone modifies the behavior of Array class such that []
becomes 1-based, for example, he breaks MY working code, which hasn't
changed.

--
Chris

Paul Prescod

unread,
Aug 11, 2001, 6:54:28 PM8/11/01
to

Chris Uzdavinis wrote:
>
>...

>
> I am still trying to figure out how one can write non-trivial
> applications in Ruby, since the interfaces can dynamically change, and
> you can't find out until runtime if your code has type-errors.

If you write non-trivial Ruby code you must have well-designed test
cases. But then if you write non-trivial C++ or Java code you need
well-designed test cases also. So I don't think there is really as deep
a difference as you think.

> What I don't know very well is how to recover. Given the dynamic
> nature of Ruby, we should be able to calculate the missing types and
> add them as necessary and try again. However, normal error-correction
> logic usually applies toward values, not types. I don't know if this
> kind of computing has been done enough to have a satisfactory general
> solution.

I'm not sure what you mean. Dynamically typed languages are roughly as
old as statically typed languages. I guess it is roughly Lisp versus
Fortran. People have indeed built many massive systems in dynamically
typed languages. It is arguably easier than in statically typed ones.

> Since functions are objects, it seems reasonable to be able to ask a
> function object "before I call you, what requirements must my
> parameters satisfy?"
>
> I'll give you a function:
>
> def foo(arg)
> # stuff
> end
>
> Now, how can I call foo such that there is no type error?

I'll give you back a function:

float foo(int b, int c){
return b/c;
}

How can I call foo such that there is no value error (when C is 0)? I
read the documentation for foo. I inspect the implementation of foo etc.
The problem can get even worse:

float foo(int *b, int *c){
return *b/*c;
}

What if b has the "type" NULL instead of the type (int *)? I'll have a
type error, right? But the compiler doesn't catch it.

>...


> If foo is dynamically created, it's my code doesn't know anything
> about it. Assuming I have to call it, if I get a TypeError exception
> how do I discover what type I should have provided? I'm not convinced
> that there is enough information available to actually infer the type.

You seem to want to write programs that dynamically assemble pieces of
code that were not tested together and which dynamically reconfigure
until they work. I don't see how this is possible in any language! How
does Java allow it? How would Java help with the b/c problem? The only
way to write code that works with other code, in general, is to
communicate with the other author through documentation or through
reading their code or something like that.
--
Take a recipe. Leave a recipe.
Python Cookbook! http://www.ActiveState.com/pythoncookbook


Martin Weber

unread,
Aug 11, 2001, 7:24:57 PM8/11/01
to
> float foo(int b, int c){
> return b/c;
> }

Yes, that's fine. But you'd get a warning for an at least implicit cast when
trying to call it with int, float.

In C++ you'd write a conversion operator for unrelated stuff you want to pass
to foo. In cases where the arguments to functions like you just given us one
could be safely typed by being related (derived) it would work fine anyways.

I mean, in some cases static types do have some neat pros (okay might be I
just feel that, been using tcl and perl for some time, as well as c/++)

And even in ruby you can get a divisionbyzero errorthingie.

Okay you can say you write a testcase at the beginning of the func and raise
an error if it does not fit a somewhat sensible baseclass. But then you've
shot yourself in the foot because you are declaring your own reflections of
static typechecking. if you do it already, there should be a global, sane way
to do it.

so it's float foo (int b, int c) return (b/c);
vs. def foo (a, b) a/b end

Both can divide by zero (oops).

You could say,
def foo (a,b)
raise 'whatever' unless (a.kind_of?(Integer)||b.kind_of?(Integer))
a/b
end

But as I said, you're just trying to imitate static typechecks...

Well, new to ruby & this list, Greetings by the way ;)
And, don't have to tell you that, but I like ruby [just like that manual
said. You'll peer and poke around the first day, and then start to love it]

Martin Weber's 5 cents (euro/100)


Paul Prescod

unread,
Aug 11, 2001, 7:43:41 PM8/11/01
to
Martin Weber wrote:
>
>...

>
> Yes, that's fine. But you'd get a warning for an at least implicit cast when
> trying to call it with int, float.

Okay, change all declarations to float. It doesn't make any difference.
The point is that division by zero can only be prevented by forethought,
testing and perhaps defensive coding. That's the same in C++, Ruby,
Lisp, Python or whatever. Therefore the removal of type declarations
does not change the basic nature of software development
code->test->code->test. All it does is remove the "build" step from the
loop.

>..


> I mean, in some cases static types do have some neat pros (okay might be I
> just feel that, been using tcl and perl for some time, as well as c/++)

There is no doubt that there are advantages to static type checking. But
the message I responded to seemed to indicate that there are certain
projects that are ONLY achievable with static type checking. I simply do
not believe that is so.

Paul Prescod

unread,
Aug 11, 2001, 8:37:10 PM8/11/01
to
Chris Uzdavinis wrote:
>
>...
>
> I'm talking about behavior and type MUTATION.

Let's say you have a library with code you do not have the
implementation for. What if it has buried within it code that totally
changes its behaviour on every Monday, Friday and Saturday. Isn't that
dynamic behaviour changing? Would you care whether it was implemented by
means of an "if" statement and a call time time() or through dynamic
method changing and "eval"?

In C I could set up a list of a thousand function pointers and in my
"statically compiled" function, all I do is take in the arguments and
call one of these pointers based on the time of day. In fact you can use
dynamic loading to make C or C++ just as dynamic as Ruby, if you are
perverse enough. You can also overwrite vtable pointers in C++ on some
platforms.

>...
> Your question is a diversion. I'm not asking how to force an errones
> call to succeed. I'm asking how, given a function (whose behavior may
> change from one call to the next) I can determine what needs to be
> passed in in order to produce a valid call.

The function's behaviour will only change if someone changes it. Either
you trust the implementor of the libraries you load or you do not. If
they change the method at runtime it will be for a good reason and to a
method with a similar signature. If you don't trust them, you shouldn't
load their code into your program.

>...
> Ruby's dynamic method mutation capabilities, means that all bets are
> off. I could be calling completely different functions from one
> invocation to the next, it can have changing requirements. Heck, it
> can have requirements that are unique every time the program is run.

Just like in C.

>...
> Huh? NULL is a value, not a type.

NULL is a value in some languages but in type theory it is usually
considered a type onto itself. After all, it can stand in for any other
type so it isn't a value of any of those types in the traditional sense.

>..
> It's an idea. Considering that Ruby programs are capable of
> dynamically changing, I'm just thinking about dynamic adaptions to
> that change. I think it's interesting to think about.

Okay, I won't bother you if you are just engaging in some
mind-experiments. But what you said was:

"I am still trying to figure out how one can write non-trivial
applications in Ruby, since the interfaces can dynamically change, and
you can't find out until runtime if your code has type-errors."

In most applications, trivial or non-trivial, the chance of an interface
dynamically changing is essentially 0. So this feature has no impact on
the ability to write non-trivial apps.

> Also, you seem to be convinced that testing is sufficient. Unless
> your tests show 100% coverage of every line of every file that your
> Ruby program uses, you don't know if one of those untested lines may
> change your environemnt and break your code. Further, even 100%
> coverage doesn't really mean much, since it could be a series of
> calls, or only particular values of certain function calls, which
> trigger the kinds of change that can be destructive.

Just like in C.

Testing doesn't guarantee you don't have bugs. It guarantees that you
catch all of the obvious bugs.

> Finally, I'm thinking about a call to eval, which opens up the world
> to unknown code. (I know about safe-levels of ruby, and tainted
> objects, but that's a band aid, not a real solution.)

If you don't like eval, just don't use it! Eval is no different than
dynamic module loading (dl_open) in C. Dynamic loading is also dangerous
if you don't trust the provider of the code you are loading. So don't
use it.

> There is no amount of testing that can prove correctness over
> dynamically changing problems.

No amount of testing can (in general) prove correctness in ANY
programming language. That's a consequence of Turing completeness.

C is demonstrably harder to test than Ruby because in C a malicious
hacker can use a buffer overflow to write code into memory and then
"eval" that code. This has actually happened pretty often in the
real-world whereas your concern about Ruby does not seem to have any
real-world consequences.

Chris Uzdavinis

unread,
Aug 11, 2001, 9:34:26 PM8/11/01
to
Paul Prescod <pa...@ActiveState.com> writes:

> There is no doubt that there are advantages to static type checking. But
> the message I responded to seemed to indicate that there are certain
> projects that are ONLY achievable with static type checking. I simply do
> not believe that is so.

That is absolutely not what I'm gettting at.

I'm searching for ways to dynamically adjust CALLING CODE runtime
behavior to be capble of responding to and adapting to dynamic change
in the functions it is calling.

Along the way I've realized that certain types of dynamic change
(note, this is unrelated to static typing nor dynamic typing) is
impossible to code against, detect, or recover from.

Someone redefining Array class's operator[], for example, will break
tested and trusted code that hasn't been touched.

(The problem is that the change occurred in a context totally removed
from the place where the symptoms are found.)

--
Chris

Nat Pryce

unread,
Aug 11, 2001, 10:05:02 PM8/11/01
to
From: "Chris Uzdavinis" <ch...@atdesk.com>

> "Nat Pryce" <nat....@b13media.com> writes:
> > In Ruby, instead of defining classes or interfaces, you define
> > protocols that objects should speak, and define those protocols
> > outside the language.
>
> This is interesting. What distinction do you make between protocol
> and interface? Can the protocol be independent from the interface?

A protocol encompasses (a) more than one interface and (b) how and when
the parties in a protocol can invoke methods on those interfaces, and what
parameters they can pass to those invocations.

> > You can then define useful classes or modules that implement those
> > protocols, but a programmer can just as easily write the protocol
> > from scratch.
>
> Yes, in that regard, every ruby function is like a C++ function
> template. (Gotta make those C++ comparisons...!)
>
> template <typename T> void foo(T arg) { /*...*/ }
>
> Rather than an "interface" which is a fixed, hard coded set of
> requirments, of which perhaps only a subset is actually used,
> templates use what they call the "concept" approach. ANY arg can be
> passed in provided that it supports all the necessary concepts which
> the code places on it. (The difference is a compile-time check is
> made, rather than runtime.)

It is similar to a C++ template in that C++ templates are turing complete
and dynamically typed. You can, if perverse enough, write complete
programs in C++ templates and have then interpreted by the C++
compiler! Similarly, Ruby programs are turing complete and dynamically
typed. However, Ruby has a human-friendly syntax and a far more
convenient development environment :-)

Just as with C++ templates, violating protocols in a dynamically typed
language can result in obscure error messages. The C++ community is
developing mechanisms to type-check templates as early as possible.
In a dynamically typed language, unit tests fulfill the same role.

> > An simple example of this kind of protocol is the relationship
> > between the methods hash and eql? or the Enumerable mixin requiring
> > the existence of the each method.
>
> These are good examples. Do you think that when we write Ruby code
> that this type of mindset should be the primary kind of issues we
> think about?

Yes.

This is the fundamental mind-set that I use when desiging OO code, whether
in a dynamically typed language or a statically typed language. A
statically
typed language lets you define interfaces or abstract classes, but they
don't
actually let you define the *protocols* by which objects interact, and so
are only of limited practical use.

> How would you suggest we recover when an argument does not support the
> protocol? Since Ruby is dynamic, object x may not support it now, but
> if you try again later it might. Or vice versa.

You don't recover. That's a programming error -- a bug. Programming errors
are the fault of the programmer and should be caught by unit tests. If an
error occurs at run time you should (a) write a unit test that fails because
of
the error and (b) fix the code so that the unit test passes. You now have
better code and a more complete suite of tests.

> There certainly is an element of uncertainty introduced along with all
> the dynamic capabilities.

Only if you don't have adequate tests. Dynamic code is a convenience,
and a great one. However, the behaviour of a package will not magically
change because it is written in dynamic code. The behaviour of the package
should be specified both in terms of protocols, and by a good suite of
unit tests.

> The trick is keeping it balanced to where
> you can know what's going on but still have flexibility.
> I still have this uneasy feeling, though, that nothing can ever be
> completely trusted, because perfectly working code can easily be
> broken by other code in another file which is completely unrelated to it.

Again, only if code is not properly specified and tested. Unit tests are an
executable specification of how code should behave.

> It seems you have to really know everything that is going on in a
> program to know if it's going to work. Though there are Modules to
> kind of separate things, it's not truly modular since if your code and
> mine are running in the same program, my code can break yours, and
> vice versa.
>
> The risk to me is not all just type, but behavior too. In a big
> application it's unreasonable to expect everyone to study every line
> of every file that everyone else is working on.
>
> So when someone modifies the behavior of Array class such that []
> becomes 1-based, for example, he breaks MY working code, which hasn't
> changed.

Again, use automatic test suites! Never do by hand that which a
computer can do automatically :-)

Cheers,
Nat.

Albert Wagner

unread,
Aug 11, 2001, 10:12:53 PM8/11/01
to
Just a newbie here, but aren't you talking about contracts?

On Saturday 11 August 2001 19:15, Chris Uzdavinis wrote:


> Paul Prescod <pa...@ActiveState.com> writes:
> > I'm not sure what you mean. Dynamically typed languages are roughly as
> > old as statically typed languages. I guess it is roughly Lisp versus
> > Fortran. People have indeed built many massive systems in dynamically
> > typed languages. It is arguably easier than in statically typed ones.
>

> Dynamic typing is only a tangent to what I'm talking about.
>
> I'm asking how to deal with dynamic mutation of behavior, the dynamic
> addition and removal of methods from objects. The dynamic
> changing-the-meaning-of-a-function-call.
>
> Dynamic typing deals with type RESOLUTION.


>
> I'm talking about behavior and type MUTATION.
>

> > > def foo(arg)
> > > # stuff
> > > end
> > >
> > > Now, how can I call foo such that there is no type error?
> >
> > I'll give you back a function:
> >
> > float foo(int b, int c){
> > return b/c;
> > }
> >
> > How can I call foo such that there is no value error (when C is 0)?
>

> Your question is a diversion. I'm not asking how to force an errones
> call to succeed. I'm asking how, given a function (whose behavior may
> change from one call to the next) I can determine what needs to be
> passed in in order to produce a valid call.
>

> The answer to your question is to call foo when c is not zero.
> Otherwise you're simply asking a loaded question. Your foo has a bug,
> and that is the implementor's responsibility.
>
> The situation I'm talking about is the CALLER's problem, not the
> function implementor's problem.


>
> > I read the documentation for foo. I inspect the implementation of
> > foo etc. The problem can get even worse:
> >
> > float foo(int *b, int *c){
> > return *b/*c;
> > }
>

> So your foo requires that its arguments be non-null pointers-to-int,
> with c being a pointer to a non-zero integral value.
>
> Once that is established, that doesn't changed. It's compiled code,
> and foos implementation doesn't change. Once you know the
> restrictions on how it works, you can write code that works within
> those restrictions.


>
> Ruby's dynamic method mutation capabilities, means that all bets are
> off. I could be calling completely different functions from one
> invocation to the next, it can have changing requirements. Heck, it
> can have requirements that are unique every time the program is run.
>

> I might THINK I know what it does, but I can never know. I just have
> to hope that no code has actually changed the meaning out from under
> my feet.


>
> > What if b has the "type" NULL instead of the type (int *)? I'll have a
> > type error, right? But the compiler doesn't catch it.
>

> Huh? NULL is a value, not a type.
>

> > > If foo is dynamically created, it's my code doesn't know anything
> > > about it. Assuming I have to call it, if I get a TypeError exception
> > > how do I discover what type I should have provided? I'm not convinced
> > > that there is enough information available to actually infer the type.
> >
> > You seem to want to write programs that dynamically assemble pieces of
> > code that were not tested together and which dynamically reconfigure
> > until they work.
>

> It's an idea. Considering that Ruby programs are capable of
> dynamically changing, I'm just thinking about dynamic adaptions to
> that change. I think it's interesting to think about.
>

> Also, you seem to be convinced that testing is sufficient. Unless
> your tests show 100% coverage of every line of every file that your
> Ruby program uses, you don't know if one of those untested lines may
> change your environemnt and break your code. Further, even 100%
> coverage doesn't really mean much, since it could be a series of
> calls, or only particular values of certain function calls, which
> trigger the kinds of change that can be destructive.
>

> Finally, I'm thinking about a call to eval, which opens up the world
> to unknown code. (I know about safe-levels of ruby, and tainted
> objects, but that's a band aid, not a real solution.)
>

> There is no amount of testing that can prove correctness over
> dynamically changing problems.
>

> > I don't see how this is possible in any language! How
> > does Java allow it? How would Java help with the b/c problem? The only
> > way to write code that works with other code, in general, is to
> > communicate with the other author through documentation or through
> > reading their code or something like that.
>

> I think you're missing my point entirely.


Niklas Frykholm

unread,
Aug 12, 2001, 3:28:06 AM8/12/01
to
On Sun, Aug 12, 2001 at 08:03:58AM +0900, Chris Uzdavinis wrote:
> I still have this uneasy feeling, though, that nothing can ever be
> completely trusted, because perfectly working code can easily be
> broken by other code in another file which is completely unrelated to it.
> [...]
> So when someone modifies the behavior of Array class such that []
> becomes 1-based, for example, he breaks MY working code, which hasn't
> changed.

In a large project you need coding standards for what people can and cannot
do. This applies to static languages as well. For example, in C++, you
can redefine the global new operator and break all sorts of things. You
could also #define malloc in a header file to do soemthing completely
different.

So just as you have a C coding standard saying "please do not redefine
the standard library or do other stupid tricks" you would have a Ruby
coding standard that said about the same thing.

The only real difference is perhaps that modifications of this kind are
easier to do in Ruby, but on the other hand most things are easier to do
in Ruby :)

// Niklas


Albert Wagner

unread,
Aug 12, 2001, 5:58:07 AM8/12/01
to
This definition from:

http://www.scism.sbu.ac.uk/law/Section5/chap1/s5c1p4.html

suits me fine.

"The use of pre and post conditions allows the development of software to be
thought of as an implicit, or explicit, contract between the calling
environment and the called subprogram. The calling environment fulfils its
part of the contract by ensuring that it only ever calls the subprogram with
a set of actual parameters which comply with the pre-condition. The
subprogram in its turn fulfils the contract by ensuring that if it is
supplied with acceptable input parameters it will always provide results
which comply with its post-condition. That is to say that it will fulfil its
advertised specification to transform its input data into the output data."


On Saturday 11 August 2001 21:29, Chris Uzdavinis wrote:


> Albert Wagner <alwa...@tcac.net> writes:
> > Just a newbie here, but aren't you talking about contracts?
>

> Maybe. What definition of contracts are you using?


ts

unread,
Aug 12, 2001, 7:37:35 AM8/12/01
to
>>>>> "M" == Mathieu Bouchard <ma...@sympatico.ca> writes:

M> mixins work with 'pointers' as well. I think the problem Guy is showing is
M> that Classes cache method lookup, but Modules don't implement Observable,
M> so Classes can't subscribe to them, so Classes can't know when Modules
M> change, and thus can't invalidate their caches.

Well, yes and no.

mixin work with 'pointers' this mean that when you write

module B; end;
module A
include B
end

Internally this is represented like this

B
^
|
A ==> (IC)

i.e. ruby has created an included class and this class (through its
pointer) make direct reference to B

This is a little different with this case

class C
include A
end

this time this will be represented like this

A B
^ ^
| |
C ==> (IC) ==> (IC)

this mean that ruby has completely linearized its search path, and the
algorithm to find a method is really simple because ruby work on a linear
path.

This is true that when a module change (via mixin), a class don't know
this modification and can't try to re-build its path.

If you look at my example, you'll see that the method that I've defined in
B has a new name, and in this case the cache is not used (i.e. this is not
a problem of cache).

Guy Decoux

p.s. : when you have seen this, the first stupid idea that came is that
ruby has made something like this

C < A < B

i.e. ruby has ordered these classes from the most specific to the least
specific and if you follow this idea you came with a *more stupid* idea,
and this idea give you the dispatch on the type :-)))


Martin Weber

unread,
Aug 12, 2001, 10:52:42 AM8/12/01
to
> External code changes can break the sort routine without touching its
> code.

What about Class.freeze ? ;) (no that is not yet in existance) And why not
treat your app as non-tainted ? I mean how could tainted code change stuff ?

Martin Weber


Chris Uzdavinis

unread,
Aug 12, 2001, 11:04:44 AM8/12/01
to
Niklas Frykholm <r2...@acc.umu.se> writes:

> The only real difference is perhaps that modifications of this kind are
> easier to do in Ruby, but on the other hand most things are easier to do
> in Ruby :)

I like the way you summarized this. Maybe I'm being too paranoid.
For the problems I've been using Ruby for it has been a terrific tool,
helping productivity and making me happy.

I don't use a lot of dynamic features like adding/removig methods at
runtime because I don't understand the ways to deal with the problems
associated. My impression so far is that nobody else knows either,
but testing is all we can hope for, so "don't make mistakes" and you
won't have to deal with them.

Perhaps that's all that can be expected.

--
Chris

Albert Wagner

unread,
Aug 12, 2001, 11:14:41 AM8/12/01
to
On Sunday 12 August 2001 09:50, Chris Uzdavinis wrote:
> > "The use of pre and post conditions allows the development of
> > software to be thought of as an implicit, or explicit, contract
> > between the calling environment and the called subprogram. The
> > calling environment fulfils its part of the contract by ensuring
> > that it only ever calls the subprogram with a set of actual
> > parameters which comply with the pre-condition. The subprogram in
> > its turn fulfils the contract by ensuring that if it is supplied
> > with acceptable input parameters it will always provide results
> > which comply with its post-condition. That is to say that it will
> > fulfil its advertised specification to transform its input data into
> > the output data."
>
> Yes, that's what I'm talking about. (Thanks for the link.)
>
> However, that's where I'm troubled. This definition indicates that
> passing in valid arguments is all that is necessary to guarnatee that
> the contract is satisified.
>
> But that's not necessarily sufficient in ruby, because the
> implementation of the function relies on basic fundamental constructs
> of the language (array indexing, hash lookup, string compare, etc) but
> someone can redefine that behavior in a non-compatible way.
>
> If I write a sort routine, and you give me a comparison function (proc
> object perhap) to use to create my ordering, and you give me
> completely valid arguments, completely correct proc object, etc, I
> still cannot guarantee that my function will result in your data being
> sorted.

>
> External code changes can break the sort routine without touching its
> code.

If I properly understand contracts, a real-world metaphor is appropriate: It
takes two to complete a contract. If I find that my co-contractor has not
fulfilled his half of the bargain, then I assume him to be untrustworthy and
will cease doing business with him. He is responsible for his product, EVEN
if he has utilized sub-contractors to build it.


Yukihiro Matsumoto

unread,
Aug 13, 2001, 12:20:10 AM8/13/01
to
Hi,

In message "[ruby-talk:19607] Re: order and freedom in Ruby (was: Re: Re: the way class variables work)"


on 01/08/12, Martin Weber <Epha...@gmx.net> writes:

|What about Class.freeze ? ;) (no that is not yet in existance) And why not
|treat your app as non-tainted ? I mean how could tainted code change stuff ?

Try the following:

class Foo
end

Foo.freeze

class Foo
def foo
end
end

matz.


0 new messages