Is there a consensus on how to check a polymorphic instance?

M

Mike Meng

I come from a very conservative background in software development
and strong checks are deeply rooted in my mind.

So do I, Dan.

I just can't imagine a `wild' object whose interface is valid can
truely do the right things. While in fact, when I typing this word, I
realize we can't ensure the internal protocol will be obeyed even when
the object IS-A base class instance.

Maybe it's time to rethink.

It seems to me the virtue of dynamic langauge is, if it looks like a
cat, it's a cat. But the problem is still there: how do you know what
it looks like before you treat it as a cat? isinstance(), , as Steve
state, is too rigid.
 
D

Donn Cave

Quoth "Dan Perl" <[email protected]>:
| | > I'm very sympathetic to the advantages of static typing
| > (NB, I read here that Python is strongly, though dynamically,
| > typed. It is not statically typed.) Rather than embrace
| > subtype polymorphism through inheritance, however, I see it
| > as evidence that no one has figured out how to make static
| > typing really work with OOP. There has to be a better way
| > to do it.
|
| You're right, I should have said statically typed instead of strongly typed.
|
| But did you really mean "no one has figured out how to make *static* typing
| really work with OOP" or did you mean *dynamic* typing?

Static. I figure dynamic & OOP get along fine -- as Smalltalk showed,
along with I believe Objective C and of course Python. Static typing
and Functional Programming seem to go hand in glove, cf. Haskell type
classes. Static OOP seems to invite inelegant and unsound compromises.

Donn Cave, (e-mail address removed)
 
D

Dan Perl

Donn Cave said:
Quoth "Dan Perl" <[email protected]>:
| | > I'm very sympathetic to the advantages of static typing
| > (NB, I read here that Python is strongly, though dynamically,
| > typed. It is not statically typed.) Rather than embrace
| > subtype polymorphism through inheritance, however, I see it
| > as evidence that no one has figured out how to make static
| > typing really work with OOP. There has to be a better way
| > to do it.
|
| You're right, I should have said statically typed instead of strongly
typed.
|
| But did you really mean "no one has figured out how to make *static*
typing
| really work with OOP" or did you mean *dynamic* typing?

Static. I figure dynamic & OOP get along fine -- as Smalltalk showed,
along with I believe Objective C and of course Python. Static typing
and Functional Programming seem to go hand in glove, cf. Haskell type
classes. Static OOP seems to invite inelegant and unsound compromises.

It's hard to argue against Smalltalk. I haven't used it for many years so I
don't remember it but it was THE language of choice for an OOP course I took
oh so many years ago. And I still see it mentioned as a model for OOP. I
never learned Objective C so I cannot argue with that either.
Unfortunately, as much as I am learning to like Python, I don't think I see
it as a picture perfect OOP language. It may be a controversial opinion but
I am not at all satisfied with encapsulation in Python (you cannot hide any
of a class's implementation).

Can you elaborate on problems that static languages have with OOP? We have
been touching on parametric polymorphism and that is a valid point. I also
see multiple inheritance as being very poorly implemented in C++ and Java to
the point where books on C++ that treat the subject discourage you from
using it and Java has effectively designed it out of the language (you can
implement many interfaces but you can extend only one class). What do you
have in mind?

Dan
 
S

Steven Bethard

Donn said:
Rather than embrace
subtype polymorphism through inheritance, however, I see it
as evidence that no one has figured out how to make static
typing really work with OOP.

I'm not sure I follow your argument here. Java interfaces provide the
same sort of protocol-based typing as Python does (though of course you
have do declare interfaces everywhere to make them work this way).
Going back to my earlier Python example:
.... for rule in rules:
.... print rule.name
....

the corresponding Java might look something like:

public interface Rule {
public Object getName();
}

public class RuleProcessor {
public void processRules(Iterable<Rule> rules){
for (Rule rule: rules) {
System.out.println(rule.getName())
}
}
}

Note that because Java is statically typed, you have to declare all the
appropriate interfaces: Iterable is in java.lang, and Rule is declared
above. In Python, the interfaces are implied by the use, and left
undeclared.

Of course, you have to declare your interfaces in a suitably generic
manner. I could have written this replacing Iterable<Rule> with
Collection<Rule> or List<Rule>, but Iterable<Rule> is the type that
makes the fewest commitments about the parameter and still allows me to
iterate over the Rule objects. Similarly, I could have written getName
to return a String, but by declaring getName with Object as a return
type, I make only the necessary commitment that getName returns a value.

The annoying thing, of course, is what you do when suddenly *one* use of
a Rule does require, say, a String result of getName:

public class OtherRuleProcessor {
public void processRules(Iterable<Rule> rules){
for (Rule rule: rules) {
String[] s = rule.getName().split('\\s')
}
}
}

Do I now declare a new interface for this Rule and add another
implements clause to all my classes that implement the original Rule as
well? Do I make the original Rule interface less general by changing
the return type from Object to String? It can get nasty rather quickly...

Python, of course, avoids this by not declaring types. =)

Steve
 
C

Carlos Ribeiro

So do I, Dan.

I just can't imagine a `wild' object whose interface is valid can
truely do the right things. While in fact, when I typing this word, I
realize we can't ensure the internal protocol will be obeyed even when
the object IS-A base class instance.

Maybe it's time to rethink.

I understand, and I found myself using isinstance more often that I
would like to admit. Being trained in OO Pascal & Delphi it comes as
no surprise...
It seems to me the virtue of dynamic langauge is, if it looks like a
cat, it's a cat. But the problem is still there: how do you know what
it looks like before you treat it as a cat? isinstance(), , as Steve
state, is too rigid.

Time for adapt(), I think...

--
Carlos Ribeiro
Consultoria em Projetos
blog: http://rascunhosrotos.blogspot.com
blog: http://pythonnotes.blogspot.com
mail: (e-mail address removed)
mail: (e-mail address removed)
 
D

Dan Sommers

On 23 Nov 2004 19:58:31 -0800,

First off, I have only been following this thread on and off, so if I'm
repeating things, I apologize.
It seems to me the virtue of dynamic langauge is, if it looks like a
cat, it's a cat ...

Google for "duck typing."
... But the problem is still there: how do you know what it looks like
before you treat it as a cat? isinstance(), , as Steve state, is too
rigid.

You don't.

Python also takes a "we're all adults here" philosophy. If your API
specifies "cat," and I pass your function an "InterfaceAdapter," it's
*my* problem.

If you're truly paranoid, make sure that your arguments at least have
the methods you're going to use:

# write 'foo' to the file-like object x; don't bother if x is not
# sufficiently file-like

def f( x ):
try: x.write
except AttributeError:
pass
else:
x.write( 'foo' )

# this works, too, but doesn't scale well (i.e., if 'foo' is some
# other non-trivial (maybe non-trusted) operation that can raise
# AttributeError):

def f( x ):
try: x.write( 'foo' )
except AttributeError:
pass

Regards,
Dan
 
P

Peter Hansen

Mike said:
It seems to me the virtue of dynamic langauge is, if it looks like a
cat, it's a cat.

I think this is more accurately expressed as "if it *acts* like
a cat, then you can treat it like a cat". Your version implies
that you really do care what it _is_, which goes back to the
static typing thinking again.
> But the problem is still there: how do you know what
it looks like before you treat it as a cat? isinstance(), , as Steve
state, is too rigid.

The argument is that you probably don't really need to know
what it looks like *before* you try to use it, even if you
think you do. The cases where you really do are probably those
Alex Martelli's recipe (mentioned near the start of the thread)
is intended to help. (Specifically, avoiding the situation
where the object implements the required protocol only partially,
but you don't find that out until you've already started using
the object, possibly corrupting it in the process.)

-Peter
 
D

Donn Cave

"Dan Perl said:
It's hard to argue against Smalltalk. I haven't used it for many years so I
don't remember it but it was THE language of choice for an OOP course I took
oh so many years ago. And I still see it mentioned as a model for OOP. I
never learned Objective C so I cannot argue with that either.
Unfortunately, as much as I am learning to like Python, I don't think I see
it as a picture perfect OOP language. It may be a controversial opinion but
I am not at all satisfied with encapsulation in Python (you cannot hide any
of a class's implementation).
Can you elaborate on problems that static languages have with OOP? We have
been touching on parametric polymorphism and that is a valid point. I also
see multiple inheritance as being very poorly implemented in C++ and Java to
the point where books on C++ that treat the subject discourage you from
using it and Java has effectively designed it out of the language (you can
implement many interfaces but you can extend only one class). What do you
have in mind?

I am really too unschooled in these matters to pursue that.
I have mentioned Haskell a couple of times, and that's my
perspective on static typing. I recommend it as an interesting
exercise: learn Haskell, find out what static typing is about.
It isn't perfect, but it's leagues ahead of C++ etc.

I don't have any beef with multiple inheritance, in fact the
notion of "mix-in" classes might have some potential to be the
basis for a more sound approach to OO.

Donn Cave, (e-mail address removed)
 
S

Steven Bethard

Donn said:
I don't have any beef with multiple inheritance, in fact the
notion of "mix-in" classes might have some potential to be the
basis for a more sound approach to OO.

The people that have a beef with multiple inheritance have it because
multiple inheritance can sometimes break encapsulation by forcing the
designer of a class to be aware of changes in the inheritance chain far
above its parents (even when no methods/attributes are added by the
change). A classic reference:

Snyder, Alan. "Encapsulation and Inheritance in Object-Oriented
Programming Languages"
http://www-plan.cs.colorado.edu/diwan/class-papers/snyder.pdf

Python tries to combat some of these problems with a good method
resolution order:

http://www.python.org/2.3/mro.html

I don't make enough use of multiple inheritance to ever have run into
these kinds of problems, but the literature in this area is enormous...

Steve
 
J

Jacek Generowicz

Dan Perl said:
Can you elaborate on problems that static languages have with OOP?

They make dynamic polymorphism impossible.

Which is why most object-oriented C++ programs are dynamically typed;
only the programmer is burdened with the work that the mostly-absent
dynamic type system should be doing.

Dynamic polymorphism crucially requires knowledge of the *run-time*
(dynamic) type of objects, in order to be able to dispatch to the
correct method. In C++ you turn your classes into dynamically typed
ones with the "virtual" keyword, which introduces a vtable and
run-time type identification (RTTI). As long as you are only
interested in calling methods which are declared in some base class,
this dynamic type systems looks almost satisfactory. As soon as you
want to use a method present in the subclass but not in the
superclass, it is up to you to faff around with dynamic_cast and
checking of the resulting pointer; all stuff which a dynamic type
system should be doing for you, but C++ makes you do yourself because
it pretends that your programs are statically typed when, in fact,
they are dynamically typed.
 
C

Christophe Cavalaria

Jacek said:
They make dynamic polymorphism impossible.

Which is why most object-oriented C++ programs are dynamically typed;
only the programmer is burdened with the work that the mostly-absent
dynamic type system should be doing.

Dynamic polymorphism crucially requires knowledge of the *run-time*
(dynamic) type of objects, in order to be able to dispatch to the
correct method. In C++ you turn your classes into dynamically typed
ones with the "virtual" keyword, which introduces a vtable and
run-time type identification (RTTI). As long as you are only
interested in calling methods which are declared in some base class,
this dynamic type systems looks almost satisfactory. As soon as you
want to use a method present in the subclass but not in the
superclass, it is up to you to faff around with dynamic_cast and
checking of the resulting pointer; all stuff which a dynamic type
system should be doing for you, but C++ makes you do yourself because
it pretends that your programs are statically typed when, in fact,
they are dynamically typed.
If your programs need that much dynamic_cast to work, then your programs are
bad. Besides, it's easy to do a 'safe' dynamic_cast, just assert that the
pointer you get isn't 0 after the cast.
 
J

Jacek Generowicz

Besides, it's easy to do a 'safe' dynamic_cast, just assert that the
pointer you get isn't 0 after the cast.

Yes, you can "easily" do the work that the absent dynamic type system
should be doing for you (and Turing equivalence tells us that that we
can implement a decent dynamic type system as well). The fact remains
that the objects *are* *dynamically* typed, even though the language
claims to be statically typed. The fact that statically typed
languages feel the need to introduce dynamic typing (however poorly
supported it may be) in order to support OOP, points to the fact that
static type systems DO have problems with OOP, which is what the
question was about.

Specifically, dynamic polymorphism is impossible without dynamic
typing.
 
D

Donn Cave

Quoth Jacek Generowicz <[email protected]>:
[ ... re C++ casting to subtype ]
| Yes, you can "easily" do the work that the absent dynamic type system
| should be doing for you (and Turing equivalence tells us that that we
| can implement a decent dynamic type system as well). The fact remains
| that the objects *are* *dynamically* typed, even though the language
| claims to be statically typed. The fact that statically typed
| languages feel the need to introduce dynamic typing (however poorly
| supported it may be) in order to support OOP, points to the fact that
| static type systems DO have problems with OOP, which is what the
| question was about.
|
| Specifically, dynamic polymorphism is impossible without dynamic
| typing.

Given a model for polymorphism that means smuggling an object
around wrapped in some degree of mystery about its type, elegant
and sound static typing does seem unlikely. But C++ and its
descendents have always been easy targets. Would you happen to
know something about how Objective CAML's OO system approaches this?

I see in the on-line documentation this paragraph:

Be aware that subtyping and inheritance are not related. Inheritance
is a syntactic relation between classes while subtyping is a semantic
relation between types. For instance, the class of colored points could
have been defined directly, without inheriting from the class of points;
the type of colored points would remain unchanged and thus still be a
subtype of points.

http://caml.inria.fr/ocaml/htmlman/manual005.html

I'm thinking that this might be more or less typical of academic
ideas about OO typing, and it seems conceptually appealing. I
don't know the details though.

Donn Cave, (e-mail address removed)
 
D

Dirk Thierbach

Donn Cave said:
Quoth Jacek Generowicz <[email protected]>:
Given a model for polymorphism that means smuggling an object
around wrapped in some degree of mystery about its type, elegant
and sound static typing does seem unlikely. But C++ and its
descendents have always been easy targets. Would you happen to
know something about how Objective CAML's OO system approaches this?

In several ways: First, by using parametric polymorphism (not defined
as in this thread above, but by allowing "type variables" (type parameters)
which are applied as necessary, both in every expression, and in
object types. Second, by using so-called "row types", i.e. types
for incomplete records (in OCaml, they are denoted by the trailing dots
in a type like < get_x : int; set_x : int -> 'a; .. > ).

Together, they allow you to statically type most of the usual applications
of inheritance, virtual methods, etc. There used to be an somewhat
more contrived example in the OCaml manual with a subject/observer
pattern, virtual methods, and both abstract and concrete classes,
but I think they removed that some time ago.

As for

I haven't heard the term "dynamic typing" in this context, and I think
it's a bit unfortunate, because it's not "typing" in the usual
sense. If Jacek just means virtual methods, as in

that's easy to do in OCaml, and it also works in the type classes
of Haskell. The problem Jacek describes here:

is solved by keeping the parametric type "uninstantiated" as long
as possible, and instantiate it with the concrete type only when
it is clear that you really need the method present in the subclass.
Or, you handle this with the row types.

It does not work in every case one can think of right from the start
(you have to be able to statically guarantee that you have really the
subclass to work with at this point of your code, and not the
superclass), so you might to rewrite your code a bit to make it
work. Maybe it's easier to see if we do concrete examples.
I see in the on-line documentation this paragraph:

Be aware that subtyping and inheritance are not related. [...]
I'm thinking that this might be more or less typical of academic
ideas about OO typing, and it seems conceptually appealing. I
don't know the details though.

The "inheritance is not subtyping"-motto is somewhat orthogonal to
this issue. It comes originally from a paper by Benjamin Pierce, with
IIRC the same title. If you think this concept through, it really helps
to sort out some of the confusion that comes with the "usual" OOL's
like Java and C++.

- Dirk
 
A

Alex Martelli

Peter Hansen said:
The argument is that you probably don't really need to know
what it looks like *before* you try to use it, even if you
think you do. The cases where you really do are probably those
Alex Martelli's recipe (mentioned near the start of the thread)
is intended to help. (Specifically, avoiding the situation
where the object implements the required protocol only partially,
but you don't find that out until you've already started using
the object, possibly corrupting it in the process.)

Exactly. The recipe's still in the CB 2nd edition (which I should be
editing right now instead of doing usenet, with deadline so terribly
close and lots of enhancements left to do, but hey, it IS 1:30 AM, I
deserve a little break;-).

The right solution is adaptation (PEP 246, Eby's PyProtocols, etc), but
until we can convince Guido of that, so that adaptation becomes
widespread, duck typing, and occasionally (when needed) "accurate LBYL"
remain the best approach.

And don't forget unit-tests -- see Robert Martin's now-famous article at
http://www.artima.com/weblogs/viewpost.jsp?thread=4639 .


Alex
 
A

Alex Martelli

Dan Perl said:
I have a question here, as we are discussing now protocol interfaces vs.
inheritance. Is using a class that implements a protocol without inheriting
from a base class still "polymorphism"?

``still'' is inappropriate here. It is _fully_ polymorphism, of course.
It's known as signature-based polymorphism. C++ has it in templates,
only, where it's the basis of the whole power of the standard library
containers, algorithms, iterators, etc. Python has it everywhere,
except where some coder breaks everything with 'isinstance' or the like.
There are probably many definitions
for polymorphism and probably all those definitions can be interpreted in
such a way that they accept also protocols. But what I would like to hear

Otherwise they're very broken and useless definitions.
is what is the general opinion of people who use python. I am biased
because I come from a C++ and Java background and I am still used to a
certain practical meaning for "polymorphism".

One that doesn't apply to C++ templates?!
But it's beginning to dawn on
me that it is only a bias and polymorphism does apply also to python
protocol interfaces. Is that generally accepted?

Yep.


Alex
 
A

Alex Martelli

Christophe Cavalaria said:
If your programs need that much dynamic_cast to work, then your programs are
bad.

Wrong. Look at the absolute mess that the Visitor design pattern is,
and how Robert Martin made it much less horrible by turning it into
Dynamic Visitor -- which needs so much dynamic_cast it hurts.

In Java, you're dynamic_cast'ing all of the time in a totally obscure
way, each time you're pulling stuff out of a containter.

SomeSillyType foo = (SomeSillyType) (mycontainer.getsomething());

since the container's method returns Object, the cast is dynamic -- only
checked at runtime. C++, and the latest Java, give up OO in favour of
another paradigm (generic programming) to deal with this, and if this
isn't an admission that their "static" (ha!) typesystem doesn't work
with OOP properly, I don't know what would count as one.
Besides, it's easy to do a 'safe' dynamic_cast, just assert that the
pointer you get isn't 0 after the cast.

or catch the dynamically generated exception in Java's case, sure. You
can "easily" implement what a decent language would have in the first
place... except that co-variance and counter-variance continue to dog
every effort to make a decent "static OOP typesystem", of course...:)


Alex
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,058
Latest member
QQXCharlot

Latest Threads

Top