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

Dynamic languages, SWOT ?

0 views
Skip to first unread message

Hugh Sasse Staff Elec Eng

unread,
Oct 20, 2000, 3:00:00 AM10/20/00
to
There has been discussion on this list/group from time to time about
Ruby being a dynamic language, rather than one with strong typing.
This is said to give greater flexibility. I'd like to understand this
idea better, and maybe then I can make better use of it.

One of the many examples given by Object Oriented practitioners, at
least some years back, was that OO would make programming safer, because
in the example of an aircraft control system, conventioaally one might
have written:

seatbelt_light = 1; /* turn on seatbelt light */
and
deploy_undercarriage = 0; /* retract undercarriage */

but one could assign 0 to the wrong variable, raising the undercarriage
when one wanted to turn off the seatbelt light, which after landing
would be expensive. With objects

seatbelt_light.retract()

could be easily detected by the compiler as being meaningless, so
the mistake (a bizarre one, but this was only an example) would be
detected really early.

That is fine. However, with dynamic languages one cannot say what
methods an object has at compile time, because we can extend objects
at run time. This makes our code more flexible, because if objects
don't do what we want we add or override methods. But it also means
that we cannot have things like design by contract, because a method
cannot say much about what it will get as parameters.

Looking at Martin Fowler's book on refactoring, I see that one thing that
OO programmers are encouraged to avoid is case statements based on the
type or other property of another object. It is recommended that
polymorphism be used, because the different types can be made subclasses
of another type, and each object in that hierarchy can respond to certain
methods in its own appropriate way. This is clearly more OO in approach.
It also assists in avoiding duplication of code, and makes things
extensible as more "subtypes" may be created. But the case statement has
an "else" (or "default") clause, so unknown types can be picked up. If
one has thrown out the case statement, presumably one has to add some kind
of assertion to check the incoming type, but that would break the dynamic
nature of the software. Or would it?

So is this situation just "one of those trade-offs one has to accept",
and it can be said that dynamic languages are better at different things
from strongly typed languages, but they are equally valid, just different?
Or is there some huge advantage that I have missed, which would make
the loss of these automatic checks a price worth paying?

As I have said, I'm not pushing for any one side, I'm trying to understand
the issues better.

Thank you,
Hugh
h...@dmu.ac.uk

Yasushi Shoji

unread,
Oct 20, 2000, 3:00:00 AM10/20/00
to
At Fri, 20 Oct 2000 19:00:06 +0900,

Hugh Sasse Staff Elec Eng <h...@dmu.ac.uk> wrote:

> That is fine. However, with dynamic languages one cannot say what
> methods an object has at compile time, because we can extend objects
> at run time. This makes our code more flexible, because if objects
> don't do what we want we add or override methods. But it also means
> that we cannot have things like design by contract, because a method
> cannot say much about what it will get as parameters.

# this is class users point of view

IMHO, in order to use a method as a class user, you need to know

- name of the method
- what parameters are required

yes, as you said, we can change methods behavior at runtime, but it's
just "we can", not "depends on computer's mood".

# it's good thing computers still don't have feelings yet ;)

[...]


> It also assists in avoiding duplication of code, and makes things
> extensible as more "subtypes" may be created. But the case statement has
> an "else" (or "default") clause, so unknown types can be picked up. If
> one has thrown out the case statement, presumably one has to add some kind
> of assertion to check the incoming type, but that would break the dynamic
> nature of the software. Or would it?

# this is class maintainer's point of view.

I'm not sure this is specific to Ruby only or came form other
language, but it is recommended to use Object#respond_to? to ensure
that the unknown object you have is capable to handle what you want.
# yes, there are different methods with same name.

e.g.

class Some
def foo(o)
raise 'oops' unless o.respond_to? :bar
:
foo.bar
end
end

> So is this situation just "one of those trade-offs one has to accept",
> and it can be said that dynamic languages are better at different things
> from strongly typed languages, but they are equally valid, just different?
> Or is there some huge advantage that I have missed, which would make
> the loss of these automatic checks a price worth paying?

IMHO, it's just different. and as usual, both side has pros and cons.

http://www.cyberdyne-object-sys.com/oofaq2/body/typing.htm#S2.4

ps. I'm no way an expert of OO languages. These are just MHO.
--
yashi


Charles Hixson

unread,
Oct 20, 2000, 3:00:00 AM10/20/00
to
Hugh Sasse Staff Elec Eng wrote:

> There has been discussion on this list/group from time to time about
> Ruby being a dynamic language, rather than one with strong typing.
> This is said to give greater flexibility. I'd like to understand this
> idea better, and maybe then I can make better use of it.
>
> One of the many examples given by Object Oriented practitioners, at
> least some years back, was that OO would make programming safer, because
> in the example of an aircraft control system, conventioaally one might
> have written:
>
> seatbelt_light = 1; /* turn on seatbelt light */
> and
> deploy_undercarriage = 0; /* retract undercarriage */
>
> but one could assign 0 to the wrong variable, raising the undercarriage
> when one wanted to turn off the seatbelt light, which after landing
> would be expensive. With objects
>
> seatbelt_light.retract()
>
> could be easily detected by the compiler as being meaningless, so
> the mistake (a bizarre one, but this was only an example) would be
> detected really early.
>

> That is fine. However, with dynamic languages one cannot say what
> methods an object has at compile time, because we can extend objects
> at run time. This makes our code more flexible, because if objects
> don't do what we want we add or override methods. But it also means
> that we cannot have things like design by contract, because a method
> cannot say much about what it will get as parameters.
>

> Looking at Martin Fowler's book on refactoring, I see that one thing that
> OO programmers are encouraged to avoid is case statements based on the
> type or other property of another object. It is recommended that
> polymorphism be used, because the different types can be made subclasses
> of another type, and each object in that hierarchy can respond to certain
> methods in its own appropriate way. This is clearly more OO in approach.

> It also assists in avoiding duplication of code, and makes things
> extensible as more "subtypes" may be created. But the case statement has
> an "else" (or "default") clause, so unknown types can be picked up. If
> one has thrown out the case statement, presumably one has to add some kind
> of assertion to check the incoming type, but that would break the dynamic
> nature of the software. Or would it?
>

> So is this situation just "one of those trade-offs one has to accept",
> and it can be said that dynamic languages are better at different things
> from strongly typed languages, but they are equally valid, just different?
> Or is there some huge advantage that I have missed, which would make
> the loss of these automatic checks a price worth paying?
>

> As I have said, I'm not pushing for any one side, I'm trying to understand
> the issues better.
>
> Thank you,
> Hugh
> h...@dmu.ac.uk

Sorry, I feel the need for clarification here. Ruby is strongly typed AND
dynamic.
Weakly typed, statically bound languages are things like Fortran60. There the
language couldn't tell the type of the item upon which you were operating, so
it made assumptions based on the name.
Strongly typed, statically bound languages are like Eiffel and Ada. The
language knows what the type MUST be at compile time (except when unusual
procedures are invoked).
Weakly typed, dynamically bound languages are ... well, there's assembler.
Can't think of anything else off the top of my head.
Strongly typed, dynamically bound languages are, among others, Ruby, Python,
Common Lisp, Smalltalk, ...
Java seems to sit (un?)comfortably in the center, leaning towards sort of
strongly typed, and sort of dynamically bound.

-- (c) Charles Hixson
-- Addition of advertisements or hyperlinks to products specifically
prohibited

Charles Hixson

unread,
Oct 20, 2000, 3:00:00 AM10/20/00
to
Hugh Sasse Staff Elec Eng wrote:

> On Sat, 21 Oct 2000, Charles Hixson wrote:
>
> > Sorry, I feel the need for clarification here. Ruby is strongly typed
> > AND dynamic.
>

> I think from what you say below, I may be using the terms imprecisely.


>
> > Weakly typed, statically bound languages are things like
> > Fortran60. There the language couldn't tell the type of the item upon
> > which you were operating, so it made assumptions based on the name.
>

> OK, I through K [?] as INTEGER, all others FLOAT.

Yes, but if you equivalence two names, then the program doesn't know which
kind of variable you have. And then there are all the variations of
parameter calls and function or subroutine declarations, which don't
necessarily match. I once ended up redefining what the value of 0 (numeric
zero!) was. (Well, that was a compiler issue, but the language didn't say
what to do.)

> ...


> > else off the top of my head. Strongly typed, dynamically bound
> > languages are, among others, Ruby, Python, Common Lisp, Smalltalk, ...
>

> This is the area I'm thinking about where the typing is not so strong
> as would ease error detection. OK, types can be dynamic, but for
> parameters it only make sense for them to be in a subset of the
> class heirarchy. There is no way to express this constraint in Ruby

This is a run time check in a dynamically bound language. I'm pretty sure
that Ruby does have ways to catch this, though they don't seem, to me, to be
quite as ?elegant?, ?standardized? as those that Smalltalk uses. Much more
similar to the techniques of Python.

> or Python. I have not really used Lisp or Smalltalk. What I mean is
> that it is a weakness [in typing?] to allow a parameter to be of *any*
> class, for example when you know it must be a Numeric
> or descendent of Numeric. So does this weakness create a different

This is a consideration that I have, also. Ada, Eiffel, and Java each
address this in some way or other. But notice that none of those are really
dynamically bound. Ruby's type system seems more akin to that of Self. Run
time checks are the only way to handle this. If you want, you could raise an
exception. How else would you want this to be handled? You can't know at
"compile" time what the type of the variable will be. But it would be nice
to be able to have some types that were bound at "compile" time. Still, how
much are you willing to have the size of the interpreter increase to add that
feature? Greater size and complexity creates slower code, even though
certain optimizations would become possible.

I think what's really practical would be a library of type checking
routines. But I haven't even thought of an appropriate generalized
specification. And it will be quite awhile before I'm doing much fancy
design in Ruby. Just today I learned about the !~ operation (i.e., no
match). But the book should be out soon, and that will help.

> strength which outweighs the inability to type-check at compilation time?
> I can see that supporting polymorphism is a strength, so that subclasses
> can be handled, but at the moment there is nothing wrong with passing in
> superclasses of the originally intended type.

Actually, there's nothing wrong with passing in a totally separately declared
class, as long as it ended up with the appropriate API. This is both a
strength and a weakness, but I think more of a strength. Consider:

There is a standard problem that models inheritance in classes and
evolution. Call it the bird problem.
Now a Rubyesque solution would be a bit different than the normal one, and
would involve mix-ins.
consider constructing separate trees of inheritance, one for descent, and the
other for capabilities. To create a species, you inherit from the
inheritance tree, and mix-in the appropriate capabilities. Thus penguins
don't end up with flight being denied, but just not being available. I'm not
sure that this is an appropriate construct, I'm still thinking about it, but
it is a way in which mix-ins more than substitute for the lack of multiple
inheritance.

> > Java seems to sit (un?)comfortably in the center, leaning towards sort
> > of strongly typed, and sort of dynamically bound.
> >
> > -- (c) Charles Hixson
> > -- Addition of advertisements or hyperlinks to products specifically
> > prohibited
> >

> Hugh
> h...@dmu.ac.uk

Hugh Sasse Staff Elec Eng

unread,
Oct 20, 2000, 12:09:40 PM10/20/00
to
On Fri, 20 Oct 2000, Yasushi Shoji wrote:

> # this is class users point of view

Yes.


>
> IMHO, in order to use a method as a class user, you need to know
>
> - name of the method
> - what parameters are required

This latter point is what I mean -- if someone passes you the wrong
parameter, or if some other "required"ed code has changed the nature
of that type (by redefining some method), then there is no protection.


>
> yes, as you said, we can change methods behavior at runtime, but it's
> just "we can", not "depends on computer's mood".

But other code you use might, or people may misuse your code.


>
> # it's good thing computers still don't have feelings yet ;)

Just over 2 months until 2001! :-)
>
> [...]


> > It also assists in avoiding duplication of code, and makes things
> > extensible as more "subtypes" may be created. But the case statement has
> > an "else" (or "default") clause, so unknown types can be picked up. If
> > one has thrown out the case statement, presumably one has to add some kind
> > of assertion to check the incoming type, but that would break the dynamic
> > nature of the software. Or would it?
>

> # this is class maintainer's point of view.

yes.


>
> I'm not sure this is specific to Ruby only or came form other
> language, but it is recommended to use Object#respond_to? to ensure
> that the unknown object you have is capable to handle what you want.

Is there any reason why this cannot be built in to the language? Each
method has its own parameters, which have method calls associated with
them, so at invokation this could be tested. There is an overhead there,
so maybe some flag would be needed, or is there a reason other than
performance why this is silly?

> # yes, there are different methods with same name.
>
> e.g.
>
> class Some
> def foo(o)
> raise 'oops' unless o.respond_to? :bar
> :
> foo.bar
> end
> end
>

> > So is this situation just "one of those trade-offs one has to accept",
> > and it can be said that dynamic languages are better at different things
> > from strongly typed languages, but they are equally valid, just different?
> > Or is there some huge advantage that I have missed, which would make
> > the loss of these automatic checks a price worth paying?
>

> IMHO, it's just different. and as usual, both side has pros and cons.

And are they therefore better at some applications, or just better for
certain styles of programming?


>
> http://www.cyberdyne-object-sys.com/oofaq2/body/typing.htm#S2.4
>
> ps. I'm no way an expert of OO languages. These are just MHO.
> --
> yashi
>

Hugh
h...@dmu.ac.uk

Hugh Sasse Staff Elec Eng

unread,
Oct 20, 2000, 12:30:38 PM10/20/00
to
On Sat, 21 Oct 2000, Charles Hixson wrote:

> Sorry, I feel the need for clarification here. Ruby is strongly typed
> AND dynamic.

I think from what you say below, I may be using the terms imprecisely.

> Weakly typed, statically bound languages are things like
> Fortran60. There the language couldn't tell the type of the item upon
> which you were operating, so it made assumptions based on the name.

OK, I through K [?] as INTEGER, all others FLOAT.

> Strongly typed, statically bound languages are like Eiffel and Ada.

> The language knows what the type MUST be at compile time (except when

All functions, parameters, and variables have types defined at compile
time. Yes, I can see that is static.

> unusual procedures are invoked). Weakly typed, dynamically bound
> languages are ... well, there's assembler. Can't think of anything

Perl4? I'm not sure.

> else off the top of my head. Strongly typed, dynamically bound
> languages are, among others, Ruby, Python, Common Lisp, Smalltalk, ...

This is the area I'm thinking about where the typing is not so strong
as would ease error detection. OK, types can be dynamic, but for
parameters it only make sense for them to be in a subset of the
class heirarchy. There is no way to express this constraint in Ruby

or Python. I have not really used Lisp or Smalltalk. What I mean is
that it is a weakness [in typing?] to allow a parameter to be of *any*
class, for example when you know it must be a Numeric
or descendent of Numeric. So does this weakness create a different

strength which outweighs the inability to type-check at compilation time?
I can see that supporting polymorphism is a strength, so that subclasses
can be handled, but at the moment there is nothing wrong with passing in
superclasses of the originally intended type.

> Java seems to sit (un?)comfortably in the center, leaning towards sort

Dave Thomas

unread,
Oct 20, 2000, 3:32:58 PM10/20/00
to
Charles Hixson <charle...@earthlink.net> writes:

> > This is the area I'm thinking about where the typing is not so strong
> > as would ease error detection. OK, types can be dynamic, but for
> > parameters it only make sense for them to be in a subset of the
> > class heirarchy. There is no way to express this constraint in Ruby
>

> This is a run time check in a dynamically bound language. I'm pretty sure
> that Ruby does have ways to catch this, though they don't seem, to me, to be
> quite as ?elegant?, ?standardized? as those that Smalltalk uses. Much more
> similar to the techniques of Python.

Well, I'm not sure what Ruby could do here, apart from implementing
protocols. You can't constrain a parameter to be an object of a
particular class: it's the methods that the object supports that is
relevant, not the class itself (as I know everyone knows--I'm just
reemphasizing the point)

So,

def append(to, what)
to << what
end

works equally well when passed a File, and Array, or a String as it's
first parameter.

Objective C had the concept of protocols, which are similar to Java's
interfaces, which specified what messages an object supports. This
_could_ be added to Ruby (I have a quick hack that implements them in
Ruby itself).

However, let me ask a question: how often do you get bitten by this in
reality? And, having been bitten, how long does it take to fix?

Personally, I find that I pass in a wrong object about once in a blue
moon, and it becomes apparent that I goofed pretty quickly.

Now ask yourself a different question. Right now, legions of Java
programmers are using

Employee emp = (Employee)staff.nextElement();

Fundamentally, how is this different from using Ruby's type system?


Regards

Dave


Charles Hixson

unread,
Oct 21, 2000, 11:47:01 PM10/21/00
to
Dave Thomas wrote:

> Charles Hixson <charle...@earthlink.net> writes:
>
> > > This is the area I'm thinking about where the typing is not so strong
> > > as would ease error detection. OK, types can be dynamic, but for
> > > parameters it only make sense for them to be in a subset of the
> > > class heirarchy. There is no way to express this constraint in Ruby
> >
> > This is a run time check in a dynamically bound language. I'm pretty sure
> > that Ruby does have ways to catch this, though they don't seem, to me, to be
> > quite as ?elegant?, ?standardized? as those that Smalltalk uses. Much more
> > similar to the techniques of Python.
>
> Well, I'm not sure what Ruby could do here, apart from implementing
> protocols. You can't constrain a parameter to be an object of a
> particular class: it's the methods that the object supports that is
> relevant, not the class itself (as I know everyone knows--I'm just
> reemphasizing the point)

Only one answer here, mainly questions:
Actually, I believe that you can implement a run-time constraint. It just isn't
obvious what kind of standardized action should be taken. So it's not clear how
the language could reasonably respond. Also, it's not clear how one should
declare a parameter to be of a type. Inheritance could be used, but isn't really
appropriate. What is really wanted is something on the order of:
The paramer must respond to the following messages: ...
and must not throw any more than the following exceptions: ...
And then what should you do if it is in violation? Currently an error is raised
(forget which one). What is a more elegant way to handle this? I don't think
that it could be reasonably detected until during execution, but I could be
wrong, so, if you could detect it, then what would you want it to do? Abort the
program? Throw and exception? Loop? Wait forever?
Throwing an exception is the best one that I've been able to think of. It would
be nice if it could be detected at an earlier stage, but then what if the branch
of the program to be followed never executed the statement in violation?
Programs often have branches that are only executed when the inputs are of a
certain specific kind.

> ...

> Objective C had the concept of protocols, which are similar to Java's
> interfaces, which specified what messages an object supports. This
> _could_ be added to Ruby (I have a quick hack that implements them in
> Ruby itself).

I think that Ruby already has such a list, but I could be confusing it with
Python (and don't know how to look this up quickly). This isn't a formalized
thing created for users. It's just that the compiler uses a list to keep track
of things, and that isn't hidden away. (If you've done that "quick hack", then
I'm sure you know what I'm referring to.)
The problem is, if you were to design a user interface to this list, what should
the API look like? Remeber that included files can change the structure of the
class (defining new methods), and that individual members of a class can have
methods added to them (I still struggle when thinking of this as a Singleton, but
I suppose that it is).
So it looks like this list doesn't only exist for the class, but also for the
members of the class. And, at least for the members, and probably for the entire
class, this list can be changed dynamically . (When WILL that book show up! ...
I've already ordered it, but it sure would be nice to have a reference right
now.)

>
>
> However, let me ask a question: how often do you get bitten by this in
> reality? And, having been bitten, how long does it take to fix?
>
> Personally, I find that I pass in a wrong object about once in a blue
> moon, and it becomes apparent that I goofed pretty quickly.
>
> Now ask yourself a different question. Right now, legions of Java
> programmers are using
>
> Employee emp = (Employee)staff.nextElement();
>
> Fundamentally, how is this different from using Ruby's type system?

The fundamental difference is that at programming time Java required knowledge
that the found element would necessarily be castable to type Employee, rather
than, say, Consultant, or Owner. Now it you had been asking about C ...

>
>
> Regards
>
> Dave

Java is an interesting case. It seems to carry enough information around that it
SHOULD be able to tell what the type of an object is. But it can't. It depends
on type declarations embedded in the program which must be program time
constants. (Program time comes prior to compile time). In C it come prior to
pre-processor time.) This wouldn't be quite as wierd as it is if it weren't for
ClassLoader. ClassLoader can take a file of compiled code, and extract the types
of the objects that it contains. So the information is in there. My guess it
that this constraint is a part of Java's security model, but I don't really
know. Anyway, making type declarations programming time constants, but not
taking advantage of the optimizations that this makes possible in Strongly
typed-Statically linked languages, is one of the design decisions that were made
in creating Java. This is the kind of choice that makes me uncertain of just
where in language space I should position it.

Dave Thomas

unread,
Oct 22, 2000, 12:32:45 AM10/22/00
to
Charles Hixson <charle...@earthlink.net> writes:

> Actually, I believe that you can implement a run-time constraint.
> It just isn't obvious what kind of standardized action should be
> taken. So it's not clear how the language could reasonably respond.
> Also, it's not clear how one should declare a parameter to be of a
> type.

I guess I'm asking: what would the point be?

If you pass in an object that you weren't expecting to receive, then
you'll get an exception thrown when you try to use it and it contains
the wrong methods. Is there much point in slowing down the language by
adding parameter checking, that merely moves this exception throwing a
few lines up the source code?

> > Objective C had the concept of protocols, which are similar to
> > Java's interfaces, which specified what messages an object
> > supports. This _could_ be added to Ruby (I have a quick hack that
> > implements them in Ruby itself).
>
> I think that Ruby already has such a list, but I could be confusing
> it with Python (and don't know how to look this up quickly). This
> isn't a formalized thing created for users. It's just that the
> compiler uses a list to keep track of things, and that isn't hidden
> away. (If you've done that "quick hack", then I'm sure you know
> what I'm referring to.)

My quick hack was different: you define a protocol as a set of method
signatures, and then say that objects must implement that protocol. It
checks dynamically as objects are mutated at runtime, so it should
keep in step.

I stopped working on it because I never really found the need to use
it.

I come back to an earlier question:

> > However, let me ask a question: how often do you get bitten by this in
> > reality? And, having been bitten, how long does it take to fix?


I guess I could see a syntax like

def fred(a : String, b : File)

which added runtime checks that 'a' and 'b' were compatible with
Strings and Files respectively, but I'd only support it if the
qualification were optional. I'd personally use it mostly as a
documentation tool.

However, remember that this still isn't type checking. In Ruby, unlike
Java and C++, classes and types are different beasts.


Regards


Dave

Hal E. Fulton

unread,
Oct 22, 2000, 3:00:00 AM10/22/00
to
----- Original Message -----
From: Dave Thomas <Da...@thomases.com>
To: ruby-talk ML <ruby...@netlab.co.jp>
Sent: Saturday, October 21, 2000 9:32 PM
Subject: [ruby-talk:5749] Re: Dynamic languages, SWOT ?


>
> However, remember that this still isn't type checking. In Ruby, unlike
> Java and C++, classes and types are different beasts.
>

Dave... please expand on that last sentence to whatever
extent you're willing.

Guy and I have been discussing classes vs. types quite
a bit.

Hal

Charles Hixson

unread,
Oct 22, 2000, 3:00:00 AM10/22/00
to
Dave Thomas wrote:

> ...


> I guess I could see a syntax like
>
> def fred(a : String, b : File)
>
> which added runtime checks that 'a' and 'b' were compatible with
> Strings and Files respectively, but I'd only support it if the
> qualification were optional. I'd personally use it mostly as a
> documentation tool.
>
> However, remember that this still isn't type checking. In Ruby, unlike
> Java and C++, classes and types are different beasts.
>
> Regards
>
> Dave

I'm not certain that I really agree with you. I think (feel?) that types and
classes actually are the same kind of thing. The simpler forms are generally
called types, and the more complex forms are generally called classes, but
the two sometimes smooge over and end up describing the same things. E.g.,
in Ruby integers are members of some class of integers (which is a small
subset of what number theorist would call an integer). In Ada, what we call
classes are referred to as tagged types, for good reasons having to do with
history and implemations.
Java makes the distinction that if it is inherited from Object then it is a
class, otherwise it is a type, but that feel like special pleading. In
Smalltalk everything is whatever they call their kinds of thing, but they are
all the same. Eiffel seems to uses basically the same distinction as Java:
"If it's built into the compiler, then it's a type, otherwise it's a class".
An argument could be made that types are the pre-defined essences, and
classes are what the user defines, but I don't particularly consider that a
useful position, except when doing rather close to the metal programming.
Somebody's got to do it, but I think that C or Ada would be a better choice
than Ruby, so ...
In this context, I feel that type and class are approximately the same.


Dave Thomas

unread,
Oct 23, 2000, 12:18:45 AM10/23/00
to
"Hal E. Fulton" <hal...@hypermetrics.com> writes:

> > In Ruby, unlike Java and C++, classes and types are different
> > beasts.
>

> Dave... please expand on that last sentence to whatever extent
> you're willing.

and Charles Hixson <charle...@earthlink.net> writes:

> I'm not certain that I really agree with you. I think (feel?) that
> types and classes actually are the same kind of thing.


OK - this is where I get to expose my old ways of thinking to general
ridicule. They go something like this:

A type is defined by a state domain and a set of operations that
(normally) act on that domain. The state domain determines the valid
set of values that objects of that type may have, and the operations
talk about how objects of the type may be manipulated.

Many languages, such as C and Fortran, have built-in types, such as
integers and floating point numbers. Here the domain is fairly
obvious, and the operations are our old friends from arithmetic.

Some languages, such as Pascal, have the ability to create new types
based on these existing types. In Pascal, these new types have the
same operations as their base types, but their domain is restricted to
some subset of their parent's domain.

Object-oriented languages extend the paradigm. Now we can define a
class, where we manage the state internally and export the operations
that manipulate and use that state. Each of these classes defines a
type.

OO languages that support subclassing have the ability to create new
types based on existing types. These new types have (at least) the
same set of operations as their base types, but their domain is
restricted. (Does this paragraph sound familiar? What a well-crafted
note, eh?)

Languages such as Java (and, less elegantly, C++), introduce a
twist. Using multiple inheritance or using interfaces, a class may
have several types simultaneously. Objects of Java's class
RandomAccessFile, for example, can be used where ever one of the types
RandomAccessFile, DataInput, or DataOutput is expected.

Statically typed languages then add compile time support for a form of
type checking. You can explicitly tell the compiler the type of object
that you are expecting as (say) a method parameter, and the compiler
can perform static analysis to see that you don't break the rule. In
the case where this static analysis is not possible (for example when
casting is involved), the compiler inserts a runtime check instead.

So, up to this point, we have a range of languages which support types
as built-in things, classes, and interfaces, often with some form of
subtyping. In these languages a class will have one or more types
associated with it. Ignoring the mess that Java makes of native types,
a type will be associated with just one class or interface.


_________ __________
| | 1 * | |
| class |-------------| type |
|_________| |__________|


So, on to Ruby, Smalltalk, and the like.

Here the picture is somewhat more subtle. At their simplest,
Ruby classes implement types, and subclasses implement subtypes. Ruby
mixins add capabilities similar to interfaces, and hence associate
multiple types with a class (for example, [1,2,3] is both an Array and
an Enumerable object).

However, Ruby differs from Java and other static languages in that it
does not check the types of objects at compile or runtime. It doesn't
care about an object's class per se. Instead, it cares that an object
implements the required methods. This leads to interesting
possibilities. For example, things that would traditionally be
implemented as subclasses in order to pass type-checking need not be
in Ruby. Instead, they can be implemented as two independent classes
that happen to implement a common set of methods (Think of the way that
you can use ARGF as a File, or ENV as a Hash, even though they are
actually just Objects.) So, Ruby immediately gives us a more complex
class/type relationship:

_________ __________
| | * * | |
| class |-------------| type |
|_________| |__________|


Many classes can implement a type, and any one class can have many
types.

Then we muddy the waters even further. In Ruby, you can extend both
classes and objects while your program is running. Say you want to
allow threads to be compared based on their priority:

class Thread
def <=>(other)
self.priority <=> other
end
include Comparable
end

Suddenly, every thread object in the system, both existing and new,
has grown a new type: they are all now Comparable as well as being
threads.

Given this tremendous flexibility, I'm not sure how an enforced system
of static tye checking could be shoe-horned in to Ruby: the paradigms
are just plain different.

In my opinion, this is not a bad thing. While programming Ruby, I have
never really felt the need for static type analysis nor for dynamic
type checking. In the face of mutable types and extendible classes,
I'm not even sure what form type checking could take.

Help me understand the other point of view.


Regards


Dave


Hugh Sasse Staff Elec Eng

unread,
Oct 23, 2000, 3:00:00 AM10/23/00
to
Trimming mercilessly...

On Sat, 21 Oct 2000, Charles Hixson wrote:

> Hugh Sasse Staff Elec Eng wrote:
>

> > On Sat, 21 Oct 2000, Charles Hixson wrote:
> >

> > parameters it only make sense for them to be in a subset of the
> > class heirarchy. There is no way to express this constraint in Ruby
>
> This is a run time check in a dynamically bound language. I'm pretty sure

I think your example below gives a good reason why...


>
> > or Python. I have not really used Lisp or Smalltalk. What I mean is
> > that it is a weakness [in typing?] to allow a parameter to be of *any*
> > class, for example when you know it must be a Numeric
> > or descendent of Numeric. So does this weakness create a different

[...]

> address this in some way or other. But notice that none of those are really
> dynamically bound. Ruby's type system seems more akin to that of Self. Run
> time checks are the only way to handle this. If you want, you could raise an
> exception. How else would you want this to be handled? You can't know at

I was thinking that some tests could be done at compile time...so
I have not considered the run-time case.

> "compile" time what the type of the variable will be. But it would be nice

I see freom below, why not.


>
> > strength which outweighs the inability to type-check at compilation time?
>

> Actually, there's nothing wrong with passing in a totally separately declared
> class, as long as it ended up with the appropriate API. This is both a
> strength and a weakness, but I think more of a strength. Consider:
>
> There is a standard problem that models inheritance in classes and
> evolution. Call it the bird problem.

Ah, the "Birds fly. Except penguins, kiwis, ostriches,.." problem

> Now a Rubyesque solution would be a bit different than the normal one, and
> would involve mix-ins.
> consider constructing separate trees of inheritance, one for descent, and the
> other for capabilities. To create a species, you inherit from the
> inheritance tree, and mix-in the appropriate capabilities. Thus penguins
> don't end up with flight being denied, but just not being available. I'm not
> sure that this is an appropriate construct, I'm still thinking about it, but
> it is a way in which mix-ins more than substitute for the lack of multiple
> inheritance.
>

At first I thought this ansered a different question, but now I see that
Mix-in effectively changes a "type", and pushes it outside the regular
class system. That fundamentally damages class based type checking.

> > Hugh
> > h...@dmu.ac.uk

Hugh Sasse Staff Elec Eng

unread,
Oct 23, 2000, 3:00:00 AM10/23/00
to
On Mon, 23 Oct 2000, Dave Thomas wrote:

> A type is defined by a state domain and a set of operations that
> (normally) act on that domain. The state domain determines the valid

[...]


> Many languages, such as C and Fortran, have built-in types, such as

[...]

> Some languages, such as Pascal, have the ability to create new types
> based on these existing types. In Pascal, these new types have the
> same operations as their base types, but their domain is restricted to
> some subset of their parent's domain.
>
> Object-oriented languages extend the paradigm. Now we can define a
> class, where we manage the state internally and export the operations

[...]

> Languages such as Java (and, less elegantly, C++), introduce a
> twist. Using multiple inheritance or using interfaces, a class may

[...]

>
> So, on to Ruby, Smalltalk, and the like.
>
> Here the picture is somewhat more subtle. At their simplest,
> Ruby classes implement types, and subclasses implement subtypes. Ruby
> mixins add capabilities similar to interfaces, and hence associate
> multiple types with a class (for example, [1,2,3] is both an Array and
> an Enumerable object).

This seems to be the crux of what is gained by having a dynamic language.
And it would seem to make type checking very difficult.


>
> However, Ruby differs from Java and other static languages in that it
> does not check the types of objects at compile or runtime. It doesn't
> care about an object's class per se. Instead, it cares that an object
> implements the required methods. This leads to interesting
> possibilities. For example, things that would traditionally be
> implemented as subclasses in order to pass type-checking need not be
> in Ruby. Instead, they can be implemented as two independent classes
> that happen to implement a common set of methods (Think of the way that
> you can use ARGF as a File, or ENV as a Hash, even though they are
> actually just Objects.) So, Ruby immediately gives us a more complex
> class/type relationship:

[...]
I had not noticed how these were implemented but they are very powerful.


>
> Many classes can implement a type, and any one class can have many
> types.
>
> Then we muddy the waters even further. In Ruby, you can extend both
> classes and objects while your program is running. Say you want to

Yes, with new methods and Mix-ins.
[...]


>
> Given this tremendous flexibility, I'm not sure how an enforced system
> of static tye checking could be shoe-horned in to Ruby: the paradigms
> are just plain different.

I think this answers my question. I have not done much with Mix-ins
myself, but have bumped into them of course. This is something I will
look into further.


>
> In my opinion, this is not a bad thing. While programming Ruby, I have

I was not trying to suggest that it was a bad thing :-). I was trying
to see why not having type checking provided some advantaage(s) that
outweighed the advantages type checking gives. I think you have answered
this pretty effectively. It could be summarized by saying it is more of
a consequence of the extra flexibility than an intentional omission, but
the power gained by sacrificing it is considerable. It can be worth
sacrificing a knight to allow a pawn to become a queen, but it doesn't
mean knights are worthless.


> never really felt the need for static type analysis nor for dynamic
> type checking. In the face of mutable types and extendible classes,
> I'm not even sure what form type checking could take.
>
> Help me understand the other point of view.

:-) I have used type checking langauges rather a lot. This was exactly
my point, I want to understand the point of view of dynamic languages.
>
>
> Regards
>
>
> Dave
>
Thank you,
Hugh
h...@dmu.ac.uk

Charles Hixson

unread,
Oct 23, 2000, 3:00:00 AM10/23/00
to
Dave Thomas wrote:

> ...A type is defined by a state domain and a set of operations that


> (normally) act on that domain. The state domain determines the valid
> set of values that objects of that type may have, and the operations
> talk about how objects of the type may be manipulated.
>

> ...


> Object-oriented languages extend the paradigm. Now we can define a
> class, where we manage the state internally and export the operations
> that manipulate and use that state. Each of these classes defines a
> type.
>
> OO languages that support subclassing have the ability to create new
> types based on existing types. These new types have (at least) the
> same set of operations as their base types, but their domain is
> restricted. (Does this paragraph sound familiar? What a well-crafted
> note, eh?)

> ...

> Help me understand the other point of view.
>
> Regards
>
> Dave

So, a class is a type that implements methods? This is the standard usage
from C++ (at least several years ago), and I was convinced by it for quite
awhile. I suppose that it was Object Oriented Software Construction (B.
Meyers, Eiffel) that changed my mind. He asserted that an external entity
shouldn't be able to look inside the class, and that it therefore shouldn't
be able to tell whether a result was returned by a function or a variable.
I read that several years ago, so I may have some of his details wrong, but
he convinced me that from the outside, one shouldn't be able to tell
whether or not a method had been called. So types and classes merged
together again.

Now, of course, this is idealistic (unless you are programming in Eiffel,
where it is true by design). But it is still what it looks like from a
distance. When one gets really close to the class, there are likely to be
routines that "know" all about it, and for efficiency's sake, sometimes
ignore the class' cell membrane. But this has to be done delicately, with
restraint, and with great care. The benefit has to be quite high before it
is justifiable. Both Python and Ruby, however, have decided that it should
be up to the programmer to use good sense about this. So if you are
looking quite closely, there is a valid difference between a type and a
class. But from any distance the concepts merge into something that looks
the same. And acts the same.

<ramble>

I realize that this particular conclusion is framed in terms of a
particular mental model of the process, and I don't see any easily
generalizeable abstraction (that's at all new). But when I think about
programs, I tend to think in terms of concrete models. They just aren't
UML diagrams, they're more like hunks of code floating around inside a cell
membrane. And one process can say "I require *that* piece" in order to
proceed with the synthesis of the answer. Of course, when one actually
gets to hooking the pieces together, the syntax is much more verbal, but
the components being hooked together is "stuff that ought to be around here
somewhere". This metaphor for programming probably can't be taken too far
(is a directory a kind of reticular membrane?), but it seems to be the one
I'm using right now.

If I easily could, I would certainly also express this in more formal
terms. But I don't naturally think that way, so the only time it usually
comes out is when I'm repeating what someone else has said. And then it
means that I haven't fully assimilated it.
</ramble>

Dave Thomas

unread,
Oct 23, 2000, 1:09:16 PM10/23/00
to
Charles Hixson <charle...@earthlink.net> writes:

> Dave Thomas wrote:
>
> > ...A type is defined by a state domain and a set of operations that


> > (normally) act on that domain. The state domain determines the valid
> > set of values that objects of that type may have, and the operations
> > talk about how objects of the type may be manipulated.
> >

> > ...


> > Object-oriented languages extend the paradigm. Now we can define a
> > class, where we manage the state internally and export the operations
> > that manipulate and use that state. Each of these classes defines a
> > type.
> >
> > OO languages that support subclassing have the ability to create new
> > types based on existing types. These new types have (at least) the
> > same set of operations as their base types, but their domain is
> > restricted. (Does this paragraph sound familiar? What a well-crafted
> > note, eh?)

> > ...


>
> > Help me understand the other point of view.
> >
> > Regards
> >
> > Dave
>

> So, a class is a type that implements methods?

I don't think that this is what I'm saying. A class is a type. The
methods are part of that type.

I don't know, put it's possible that I'm talking about types and
you're talking about values.

> I suppose that it was Object Oriented Software Construction (B.
> Meyers, Eiffel) that changed my mind. He asserted that an external entity
> shouldn't be able to look inside the class, and that it therefore shouldn't
> be able to tell whether a result was returned by a function or a
> variable.

This is two separate things: information hiding (encapsulation) and
the uniform access principle. Both are good.

> So types and classes merged together again.

Were they ever separate?

> When one gets really close to the class, there are likely to be
> routines that "know" all about it, and for efficiency's sake, sometimes
> ignore the class' cell membrane.

But we're not talking about encapsulation, we're talking about type
checking.


Regards


Dave

0 new messages