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

[platform-design] Encapsulation

3 views
Skip to first unread message

David A. Cornelson

unread,
Mar 31, 2003, 5:40:57 PM3/31/03
to
Is encapsulation obsolete in IF languages? It would seem that the nature of
IF objects is that they wouldn't need it. The primary benefit or reason to
use encapsulation is to black-box the implementation of how data gets
changed within an object, but is that important in IF?

Dave


Adam Thornton

unread,
Mar 31, 2003, 6:12:00 PM3/31/03
to
In article <7vmcnQ6V1qr...@speakeasy.net>,

It's certainly important if you're putting together a library for people
to use. For an actual game, not so much. Although I had great fun
designing the eponymous object in SMTUC as a
strict-separation-of-interface-and-implementation item.

Adam

Kevin Forchione

unread,
Mar 31, 2003, 7:58:27 PM3/31/03
to

"David A. Cornelson" <david dot cornelson at iflibrary dot com> wrote in
message news:7vmcnQ6V1qr...@speakeasy.net...

Well, this is an important question, since neither Inform, nor TADS adopts
the black-box approach. Certainly the issue of protecting the internal
details of implementation were discussed in TADS 3, but were not persued.
And Inform, which does provide such features, does not make use of them in
the library.

Rather remarkable, don't you think?

Interactive fiction appears to decouple the hiding of an implementation's
internals; and the separation of interface and implementation. Given the
peculiar, quasi-singleton nature of the object model, perhaps this is
natural? Black-boxing the internal details would, if I can use a loose
analogy, be a little like implementing the parser in the 'terp and providing
a few hooks for overrides.

--Kevin


Daniel Barkalow

unread,
Mar 31, 2003, 9:45:25 PM3/31/03
to
On Mon, 31 Mar 2003, it was written:

With the idea that you're going to have two languages, one for games and
one for the system, there's no reason for encapsulation in the former
(since the game code is not going to be conventionally reuseable, as it
primarily contains a particular story), but it might be useful in the
latter. Certainly a number of things people trip on with Inform are cases
where they poke the library in ways that it ought to prohibit (e.g., "move
player to <location>").

Of course, no language *needs* encapsulation; you can just document that
code that does a particular thing won't necessarily work. But it's nice
for some things.

-Iabervon
*This .sig unintentionally changed*

Neil Cerutti

unread,
Apr 1, 2003, 11:44:56 AM4/1/03
to
In article <Tu5ia.396$2L7...@newssvr16.news.prodigy.com>, Kevin

I agree. Inform gained interface flexibility by being so free
with its internals. But the library code is heavy concrete as a
result. Implementation changes in the library will, for example,
break most substantial library contributions.

But even if encapsulation will not be used much at all, and it
doesn't seem to be very popular judging by IF code I've seen, I
think a new system should provide some means of encapsulation, as
long as it's not terribly difficult to add.

Here's one example.

There is no useful interface to the parse buffer in Inform. It
was an apparently fine decision, at the time, to include a very
minimal interface to the parse buffer (basically NextWord() is
all that you get), since the parse buffer was documented in the
Z-machine specification and in some ways maintained by Z-machine
instructions. But recently Andrew Plotkin's Glulx virtual machine
and retargeted Inform compiler have revealed that an interface to
the parse buffer might have been a good idea after all.

Counting text in the buffer, reading actual text typed by the
player, and especially manipulating text in the buffer is
complicated, and now, if you wish your library contribution to
work for both Glulx and Inform, you've got to write that tricky
code twice.

So on the whole, I vote for increased encapsulation of library
code in future systems, coupled with wider and better interfaces.

--
Neil Cerutti

Mike Roberts

unread,
Apr 1, 2003, 3:02:36 PM4/1/03
to
"Neil Cerutti" <hor...@yahoo.com> wrote:
> > "David A. Cornelson" <david dot cornelson at iflibrary dot com>:

> >> Is encapsulation obsolete in IF languages?
>
> So on the whole, I vote for increased encapsulation of library
> code in future systems, coupled with wider and better interfaces.

I think you've hit the nail on the head when you talk about interfaces. To
me, it's much more important in an IF context for the library to expose good
interfaces than it is to have formal implementation-hiding mechanisms in the
language. Language-level implementation-hiding mechanisms can be good tools
for enforcing proper use of interfaces - but mostly in the area of ensuring
that library clients are using library interfaces as the library designers
intended. They're less good for forcing library authors to use good OO
design principles in the first place; anyone who's done much C++ or java
coding has probaby run into classes where all of the member variables were
scrupulously defined as 'private', but then each had a public getter/setter
method that did nothing more than read and write the variable.

The thing about IF that makes formal implementation-hiding undesirable, I
think, is that it seems unavoidable for every game to become an organic
extension of the library. IF authors want to customize everything - at
least, each author wants to customize a few things, at such depth that they
need access to the library's internal implementation. Everyone's obviously
better off to the extent that this can be minimized through abstractions in
the library (in the form of exposed interfaces), but I'm skeptical that it's
possible (or desirable) to write a library so general and all-encompassing
that few authors will ever want to tinker with its internals.

--Mike
mjr underscore at hotmail dot com

Neil Cerutti

unread,
Apr 1, 2003, 4:03:43 PM4/1/03
to
In article <pkmia.11$Pt1...@news.oracle.com>, Mike Roberts
wrote:

> "Neil Cerutti" <hor...@yahoo.com> wrote:
>> > "David A. Cornelson" <david dot cornelson at iflibrary dot
>> > com>:
>> >> Is encapsulation obsolete in IF languages?
>>
>> So on the whole, I vote for increased encapsulation of library
>> code in future systems, coupled with wider and better
>> interfaces.
>
> I think you've hit the nail on the head when you talk about
> interfaces. To me, it's much more important in an IF context
> for the library to expose good interfaces than it is to have
> formal implementation-hiding mechanisms in the language.
> Language-level implementation-hiding mechanisms can be good
> tools for enforcing proper use of interfaces - but mostly in
> the area of ensuring that library clients are using library
> interfaces as the library designers intended. They're less
> good for forcing library authors to use good OO design
> principles in the first place; anyone who's done much C++ or
> java coding has probaby run into classes where all of the
> member variables were scrupulously defined as 'private', but
> then each had a public getter/setter method that did nothing
> more than read and write the variable.

Not that there's anything *wrong* with that. ;-)

Actually, I have changed the subject. Sorry to the OP.

> The thing about IF that makes formal implementation-hiding
> undesirable, I think, is that it seems unavoidable for every
> game to become an organic extension of the library. IF authors
> want to customize everything - at least, each author wants to
> customize a few things, at such depth that they need access to
> the library's internal implementation. Everyone's obviously
> better off to the extent that this can be minimized through
> abstractions in the library (in the form of exposed
> interfaces), but I'm skeptical that it's possible (or
> desirable) to write a library so general and all-encompassing
> that few authors will ever want to tinker with its internals.

What if I were to change the word "want" in your last sentence to
"need"?

--
Neil Cerutti

Mike Roberts

unread,
Apr 1, 2003, 5:51:08 PM4/1/03
to
"Neil Cerutti" <hor...@yahoo.com> wrote:
> mjr:
> > [...] I'm skeptical that it's possible (or desirable) to write

> > a library so general and all-encompassing that few authors
> > will ever want to tinker with its internals.
>
> What if I were to change the word "want" in your last
> sentence to "need"?

I guess it depends on whether you mean (1) the author *could* achieve *any*
desired effect using the library's intended abstractions, but the author
doesn't realize it and unnecessarily hacks the library instead, or (2) the
author doesn't really *need* to achieve the effect they want, in that they
could write a perfectly good game (albeit not the one they had in mind) that
lives within the constraints of the library. I assume you mean (1), because
clearly the utility, or least the popularity, of any authoring system
depends on its being able to achieve a fairly wide range of effects that
authors merely want.

So if you mean (1), then I'd still be skeptical that it's possible in
practice to write a library that's truly general enough to satisfy everyone.
I'm sure you can get much closer than the current state of the art; your
example of the Inform text buffer is a clear case where an improved library
abstraction would eliminate the need for direct tampering in the vast
majority of cases. Even for this example, though, I'd bet you could find a
few existing games that do something really unusual that you wouldn't allow
for in the first few revs of the interface. Maybe if you carefully
considered the whole universe of existing games, you could come up with an
interface that satisfied every existing game - but even if you did, I bet
someone would eventually come along needing something outside of what was
considered in that interface. There's another trade-off that might stop you
from even accounting for all existing games, though: there's often a point
where more abstraction makes an interface harder to use - and almost from
the start, more abstraction makes it harder to learn. If the library is as
wide open as IF libraries tend to be, without any formal
implementation-hiding, the library designer can always say: okay, we *could*
account for this additional range of fringe possibilities, but that would
make things more complicated than most people will really want or need;
let's just leave it out, since we know people can always hack the library if
they really want this.

I certainly don't mean to imply that I don't like proper abstractions or
that I think proper OO implementation-hiding is bad, or anything like that.
It's just that my experience with tads 2, which had fantastically
well-enforced implementation-hiding in the parser by virtue of the parser
being written in C, has been that no matter how many abstractions and hook
points I added, there was always someone coming along with a problem that
required something more. The lesson I took was that the parser really has
to be part of the library, written in the game language, so that authors
will be able to hack the parser as a last resort. I suppose an OO purist
might tell me that I learned the wrong lesson, that I shouldn't give up on
perfect OO nirvana just because my own attempts have fallen short. My
defense against that accusation is that I've only given up on the perfection
part.

Neil Cerutti

unread,
Apr 2, 2003, 10:50:36 AM4/2/03
to
In article <pOoia.12$Pt1...@news.oracle.com>, Mike Roberts
wrote:

> "Neil Cerutti" <hor...@yahoo.com> wrote:
>> mjr:
>> > [...] I'm skeptical that it's possible (or desirable) to
>> > write a library so general and all-encompassing that few
>> > authors will ever want to tinker with its internals.
>>
>> What if I were to change the word "want" in your last sentence
>> to "need"?
>
> I guess it depends on whether you mean (1) the author *could*
> achieve *any* desired effect using the library's intended
> abstractions, but the author doesn't realize it and
> unnecessarily hacks the library instead, or (2) the author
> doesn't really *need* to achieve the effect they want, in that
> they could write a perfectly good game (albeit not the one they
> had in mind) that lives within the constraints of the library.
> I assume you mean (1), because clearly the utility, or least
> the popularity, of any authoring system depends on its being
> able to achieve a fairly wide range of effects that authors
> merely want.

Yes, I meant the former.

> So if you mean (1), then I'd still be skeptical that it's
> possible in practice to write a library that's truly general
> enough to satisfy everyone.

I know you have a lot of experience in this area, so I'm inclined
to believe you. I just wanted to make sure I knew what you meant.

> I'm sure you can get much closer than the current state of the
> art; your example of the Inform text buffer is a clear case
> where an improved library abstraction would eliminate the need
> for direct tampering in the vast majority of cases.

What might be more interesting would be if I could go back and
discover *why* a specific author needed to manipulate the parse
buffer, and add *that* as an interface instead.

One specific instance I recall is a library contribution called
AskTellOrder (by Irene Callaci) that minpulates:

>TED, GO NORTH

and

>ASK TED TO GO NORTH

by examining and possibly changing the text in the parse buffer
so that they become the same input.

This solves the problem in Inform that the two sentences, though
probably semantically equivalent to the player, result in
completely different messages getting sent to Ted.

This sort of thing could have been provided for in a better way,
possibly with a global 'TranslateAction' routine, in which an
author had a chance to manipulate actor, action, noun and second
before final dispatch.

The Inform parser itself takes that liberty in some cases (it
translates "give fred biscuit" into "give biscuit to fred", and
"fred, tell me about x" into "ask fred about x", but it's not
open to the author to do so except by hacking InformLibrary.play,
or clobbering the parse buffer using BeforeParsing as Irene did.

> I certainly don't mean to imply that I don't like proper
> abstractions or that I think proper OO implementation-hiding is
> bad, or anything like that. It's just that my experience with
> tads 2, which had fantastically well-enforced
> implementation-hiding in the parser by virtue of the parser
> being written in C, has been that no matter how many
> abstractions and hook points I added, there was always someone
> coming along with a problem that required something more.

I'm glad you brought that up, because I remember the threads that
were common in those days, but I'm not a TADS programmer, so I
didn't feel comfortable bringing it up. But even before TADS 3,
the number of those threads had decreased significantly after the
introduction HTML-TADS, if I recall correctly. So your expanded
interface certainly satisfied a lot of people, if not everybody.

> The lesson I took was that the parser really has to be part of
> the library, written in the game language, so that authors will
> be able to hack the parser as a last resort. I suppose an OO
> purist might tell me that I learned the wrong lesson, that I
> shouldn't give up on perfect OO nirvana just because my own
> attempts have fallen short. My defense against that accusation
> is that I've only given up on the perfection part.

I agree having the library code written in the game's language is
a big win.

--
Neil Cerutti

Kevin Forchione

unread,
Apr 3, 2003, 2:49:55 AM4/3/03
to
"Neil Cerutti" <hor...@yahoo.com> wrote in message
news:b6f0sc$4jql4$1...@ID-60390.news.dfncis.de...

> What might be more interesting would be if I could go back and
> discover *why* a specific author needed to manipulate the parse
> buffer, and add *that* as an interface instead.

Ah, but then a paradox of sort ensues. In their search for "originality",
authors push the boundaries of systems. It's a never-ending spiral. Add a
feature, and like the hydra it grows heads. The clearest example is the
progression we've had with IF systems. TADS 3 won't be an exception here,
it's complexity will invite more directions to push in.

--Kevin


Arnel Legaspi

unread,
Apr 4, 2003, 3:00:07 AM4/4/03
to
David A. Cornelson wrote:

Hi!

What do you guys mean with "black-boxing the implementation of how
data gets changed within an object"? I'd like to understand where you
guys are going along with this subject.

Thanks!

Arnel
*Newbie IF programmer*

Ricardo SIGNES

unread,
Apr 4, 2003, 8:18:25 AM4/4/03
to
In article <39ff5340.03040...@posting.google.com>, Arnel Legaspi wrote:
> David A. Cornelson wrote:
>
>> Is encapsulation obsolete in IF languages? It would seem that the nature of
>> IF objects is that they wouldn't need it. The primary benefit or reason to
>> use encapsulation is to black-box the implementation of how data gets
>> changed within an object, but is that important in IF?
>
> What do you guys mean with "black-boxing the implementation of how
> data gets changed within an object"? I'd like to understand where you
> guys are going along with this subject.

I will grossly simplify this. Encapsulation is an aspect of object-oriented
programming. Data is "encapsulated" within objects and must be made available
explicitly. If I have a series of objects representing dogs, with the
properties (breed, weight, name), and methods #get_breed and #get_name, then
the dogs' weights are not available to other objects. They are /encapsulated/.

David is asking whether IF languages need encapsulation. (I'm not sure
obsolete is the right word to use, but I think I get his question.) I think
that they do not. Encapsulation is useful for security (because objects cannot
access or modify private data, so they can be shared with confidence) and for
encouraging good code (because programmers using objects are forced to use
method calls rather than direct object-munging). In general, I would venture
to say that IF languages don't have these concerns. My piece of IF is unlikely
to need to import a secure object from one of David's pieces. Also, the cost
of encapsulation (the coder must implement methods to read and write private
data) is not really trivial. I don't think it's worth it.

Someone said (roughly), regarding Perl's lack of encapsulation, "You should
stay out of my living room because you're polite, not because I have a
shotgun."

--
rjbs

Quintin Stone

unread,
Apr 4, 2003, 9:05:54 AM4/4/03
to
On Fri, 4 Apr 2003, Ricardo SIGNES wrote:

> Someone said (roughly), regarding Perl's lack of encapsulation, "You should
> stay out of my living room because you're polite, not because I have a
> shotgun."

Bwahahaha, I love it.

/====================================================================\
|| Quintin Stone O- > "You speak of necessary evil? One ||
|| Code Monkey < of those necessities is that if ||
|| Rebel Programmers Society > innocents must suffer, the guilty must ||
|| st...@rps.net < suffer more." -- Mackenzie Calhoun ||
|| http://www.rps.net/ > "Once Burned" by Peter David ||
\====================================================================/

David A. Cornelson

unread,
Apr 4, 2003, 9:22:27 AM4/4/03
to
"Neil Cerutti" <hor...@yahoo.com> wrote in message
news:b6k303$61j7s$1...@ID-60390.news.dfncis.de...

> In article <39ff5340.03040...@posting.google.com>,
> Arnel Legaspi wrote:
> It's the useful discipline of hiding the details of how a
> procedure is carried out from a user.
>
> The "black box" that David refers to is (I think) a mathematical
> analogy for a function for which we don't know it's details.
>
> F(4, 0) = 0
> F(7, 1) = 2
> F(1, 4) = 16
> F(2, 4) = 65536
>
> The concept is useful to both the programmer and the user. The
> programmer is allowed to change the details of the function F as
> long as the result is the same. The user is allowed to think of
> the function as an abstraction, which makes it easier to think
> about larger processes.
>
> Without "black-boxing" the function, the user will still be able
> to think of the function as an abstraction, but the programmer
> would lose some ability to improve the function, since users may
> write code that depends on the (visible) details.
>
> It's a bit of a trade-off, taking power from users and conferring
> it on the programmer. Presumably, the programmer will use this
> power for good, thus benefitting everybody. In other words, if
> the programmer provides good enough abstractions to the user, the
> user will not need or want to see the details.
>

This pretty much hits the nail on the head. I see encapsulation as an all or
nothing replacement rule.

If I offer a class with several base methods, there are several ways a
developer can "modify" the personality of this class.

1. Replace the entire class. Encapsulation would be meaningless here since
we never got deep enough to look at the actual methods or member data. But
the programmer _is_ responsible for creating at least the same interfaces
that are used within the system/library/game.

2. Change code in the class. This is problematic to me and this is generally
how things are done in Inform (or #1). If I later need the original method
logic, there doesn't seem to be a way to have my cake and eat it too.

3. Inheritence. I've created a whole new class and replaced methods with my
own functionality. The interface is the same, but I've written my own
internals. This seems like a big win to me.

4. Overloading. I like the original method, but I think I have a second way
of doing things. I'd like to have a signature determine which method is
called. Another win for me.

Which is the best scenario? Hard to say. I like 3 and 4. It seems that
without encapsulation, we're _stuck_ with 1 or 2.

I could be wrong. Someone else want to pass though this logic and tell me
what I missed?

Dave


Kevin Forchione

unread,
Apr 4, 2003, 11:53:21 AM4/4/03
to
"David A. Cornelson" <david dot cornelson at iflibrary dot com> wrote in
message news:vrednSGOxv4...@speakeasy.net...

>
> This pretty much hits the nail on the head. I see encapsulation as an all
or
> nothing replacement rule.
>
> If I offer a class with several base methods, there are several ways a
> developer can "modify" the personality of this class.
>
> 1. Replace the entire class. Encapsulation would be meaningless here since
> we never got deep enough to look at the actual methods or member data. But
> the programmer _is_ responsible for creating at least the same interfaces
> that are used within the system/library/game.
>
> 2. Change code in the class. This is problematic to me and this is
generally
> how things are done in Inform (or #1). If I later need the original method
> logic, there doesn't seem to be a way to have my cake and eat it too.
>
> 3. Inheritence. I've created a whole new class and replaced methods with
my
> own functionality. The interface is the same, but I've written my own
> internals. This seems like a big win to me.
>
> 4. Overloading. I like the original method, but I think I have a second
way
> of doing things. I'd like to have a signature determine which method is
> called. Another win for me.
>
> Which is the best scenario? Hard to say. I like 3 and 4. It seems that
> without encapsulation, we're _stuck_ with 1 or 2.

Oh, I don't know, TADS makes great use of #3 without using encapsulation
(hiding their internal workings). In fact, TADS 'modify' keyword is
essentially a shorthand for #3, in that it creates an unnamed internal class
from which the 'modified' code then inherits.

--Kevin


Mike Roberts

unread,
Apr 4, 2003, 2:27:56 PM4/4/03
to
David A. Cornelson wrote:
> If I offer a class with several base methods, there are several ways
> a developer can "modify" the personality of this class.
> [...]

> 3. Inheritence. I've created a whole new class and replaced methods
> with my own functionality. The interface is the same, but I've written
> my own internals. This seems like a big win to me.

I think this is more typically known as polymorphism rather than
inheritance. Inheritance usually refers to incremental modification of a
base class by overriding some, but not all, of the methods of the base
class, and sometimes invoking the original base class implementation as a
subroutine of an override (the java "super.xxx()" or C++
"myBaseClass::xxx()" syntax). Inheritance in this sense usually involves
extending rather than replacing the base class implementation - the
"protected" access modes in java and C++ are specifically intended to
support this kind of design, because they let the base class make all or
parts of its internals accessible to subclasses but not to client code.

But at any rate, whatever you want to call it, I'd challenge your
conclusion:

> It seems that without encapsulation, we're _stuck_ with 1 or 2.

I really don't think encapsulation (in the implementation-hiding sense) is a
necessary part of your design mode (3). What you're describing is defining
a public interface to some functionality such that you can provide multiple
unrelated implementations of the same interface, and such that the correct
implementation is chosen dynamically on invocation according to which object
instance is being used. That's independent of access control, because
you're just talking about calling a public interface. If there's no
implementation-hiding, that just means that everything's public; you can
obviously still have your interface be public if everything's public.

Implementation-hiding access controls are just tools that let you as the
interface implementer specify declaratively which parts are indeed meant for
public consumption, thus notifying clients of your interface (via compiler
errors) when they unwittingly create a dependency on your implementation's
internals. The same thing can be accomplished with documentation and
discipline on the part of the clients. Programmers often let expediency
override proper discipline, so compiler-supported access controls tend to be
more effective; but even so, access controls are merely an enhancement to
polymorphism, not a necessary ingredient.

As for your other preferred design mode, signature overloading, that also
seems to have nothing to do with implementation hiding. In what way would
the presence of absence of formal access controls affect signature
overloading?

Joe Mason

unread,
Apr 5, 2003, 2:44:52 PM4/5/03
to
In article <slrnb8r1h0.qv...@humptydumpty.manxome.org>, Ricardo SIGNES wrote:
> Someone said (roughly), regarding Perl's lack of encapsulation, "You should
> stay out of my living room because you're polite, not because I have a
> shotgun."

The problem with that is that programmers aren't polite. And they don't
read manuals.

Joe

David Thornley

unread,
Apr 7, 2003, 11:39:59 AM4/7/03
to
In article <vrednSGOxv4...@speakeasy.net>,

David A. Cornelson <david dot cornelson at iflibrary dot com> wrote:
>
>This pretty much hits the nail on the head. I see encapsulation as an all or
>nothing replacement rule.
>
Why? There are languages with different levels of encapsulation, and
even a language with strictly enforced encapsulation can still allow
objects to be modified piecemeal.

>If I offer a class with several base methods, there are several ways a
>developer can "modify" the personality of this class.
>
>1. Replace the entire class. Encapsulation would be meaningless here since
>we never got deep enough to look at the actual methods or member data. But
>the programmer _is_ responsible for creating at least the same interfaces
>that are used within the system/library/game.
>

Right. This means that the interface should be well-documented. This
is true of any system: if a library or system doesn't document its
interface, users will treat any aspect of visible behavior as part
of the interface. The nice thing about some sort of formal encapsulation
here is that the library designer can restrict use of the interface.

>2. Change code in the class. This is problematic to me and this is generally
>how things are done in Inform (or #1). If I later need the original method
>logic, there doesn't seem to be a way to have my cake and eat it too.
>

It can also cause problems with upgrades. If you're working on a game,
and a system upgrade comes out that solves some problems you have, and
you've already heavily modified parts of the library, you may have a
lot of work to do.

>3. Inheritence. I've created a whole new class and replaced methods with my
>own functionality. The interface is the same, but I've written my own
>internals. This seems like a big win to me.
>

Right. As Mike Roberts pointed out, this is much more useful with
polymorphism. One issue is that this may clash with a library
inheritance hierarchy, unless supported in the library.

>4. Overloading. I like the original method, but I think I have a second way
>of doing things. I'd like to have a signature determine which method is
>called. Another win for me.
>

This is not using the interface but extending it. This rather violates
encapsulation, and implies a chummier relationship with the implementation
than is usually desireable. This is not to say it's a bad idea, but
it isn't related to encapsulation.

>Which is the best scenario? Hard to say. I like 3 and 4. It seems that
>without encapsulation, we're _stuck_ with 1 or 2.
>

Nope. Common Lisp, for example, has optional encapsulation, but it is
easy to use inheritance and polymorphism and write new methods with
different signatures.

>I could be wrong. Someone else want to pass though this logic and tell me
>what I missed?
>

I'm not sure what you're thinking of encapsulation as. You seem to
be talking about something desirable, but it isn't encapsulation.

--
David H. Thornley | If you want my opinion, ask.
da...@thornley.net | If you don't, flee.
http://www.thornley.net/~thornley/david/ | O-

Cedric Knight

unread,
Apr 8, 2003, 7:47:15 AM4/8/03
to
"David A. Cornelson" wrote

Some thoughts:

I've not been a great fan of object-orientation in general languages,
but would say that (a) encapsulation (which I thought originally meant
binding code to data structures), and (b) the hiding of private
properties which encapsulation enables, are very useful but not
essential in the IF model world.

There are a class of bugs that might be avoided by use of an
appropriate method rather than altering object properties. E.g. for
some reason you want to impose an inventory limit on the PC; however,
you include some piece of Inform code reading 'move object to player',
thereby bypassing any check of the limit, whether in the library or your
own code. Minor errors like this are common, and without the programmer
defining interfaces to their objects (via encapsualted methods or
general purpose routines), a lot of 'special case' programming is needed
to ensure the consistency of the model world.

In more simulationist IF, it would be good to have
actions regulated by a single method unique to an object or class of
objects (TADS seems better in this respect than Inform). There is an
irritating asymmetry here, though: should the programmer use
'player.receive(coin)' or 'coin.insertin(player)'? Which of the 2 or 3
objects involved in a complex action owns the appropriate method and how
does the programmer remember this? It is likely that any <Transfer coin
player> action will have to check both a method in the coin and the
player, but what is the hierarchy of this procedure; which is the
fully-validated method, and which is the primitive? Without a strict
convention and lots of forethought here, the player object is going to
have to be declared a 'friend' to the coin object, and vice versa, and
you might as well not have private properties at all.

A special case in IF is setting up the initial state of the world. The
usual way of doing this in Inform is directly accessing all properties
so that they could in contain non-validated data; but bugs produced this
way are easy to trace, and it's not *substantially* different from a
more OO way of doing things, like:

Cockpit= new Room("You see dials, levers & twiddly things.", new
Path(out, Cabin), new Rule(jump, "You bang your head"),...);

Another thing that useful encapsulation in IF entails is multiple
inheritance (so that e.g. something can be both takeable and a
container). I agree with what I think David was saying about
inheritance and polymorphism/virtual methods - it's useful for an author
to be able to make near-clones of another IF object without having to
cut and paste code and then separately maintain it; however, this does
not require having 'private parts' (as the designer of Ada wanted to
call them) for which the main benefit IMHO is validation.

CK


Edmund Kirwan

unread,
Apr 8, 2003, 3:55:02 PM4/8/03
to
"Cedric Knight" <ckn...@gn.babpbc.removeallBstosend.org> wrote in message news:<3Gyka.802$xd5.1...@stones.force9.net>...
> "David A. Cornelson" wrote

>
> In more simulationist IF, it would be good to have
> actions regulated by a single method unique to an object or class of
> objects (TADS seems better in this respect than Inform). There is an
> irritating asymmetry here, though: should the programmer use
> 'player.receive(coin)' or 'coin.insertin(player)'? Which of the 2 or 3
> objects involved in a complex action owns the appropriate method and how
> does the programmer remember this?

Ok, the player-coin thing was just an example, and it shouldn't be
enlarged upon ... but what the hell.

To overcome this, "Irritating asymmetry," you've to think far more OO
than this.

The answer to, "Which of the 2 or 3 objects ... owns the apt method,"
is not one of them. The object that will own the special logic will be
the object that's an instance of the player checking his pockets and
finding a coin.

With glorious OO (and we're not talking TADS or Inform here, just some
hypothetical OO system), the verbs will be objects, too. In this case,
the Take verb will have a user (player) and a Thing (coin), and it
will (effectively) do a player.receive(coin).

But that's not where the special logic of saying, "Wow, I'm rich!"
will lie.

There will also be Behaviour objects (actually, they're classes). And
one subclass of this Behaviour class will be a CheckWhatIHaveBehaviour
class. And each Behaviour class can be have an associated Consequence
class. And one subclass of this Consequence class will be a
ShoutConsequence.

And to glue it all together, the Player class can have as many
associated Behaviour classes as it likes. So the startup will look
like:

coin = new Coin;
player = new Player;
shoutConsequence = new ShoutConsequence("Wow, I'm rich!");
haveCoin = new CheckWhatIHaveBehaviour();
haveCoin.add(coin);
haveCoin.addConsequence(shoutConsequence);
player.addBehaviour(haveCoin);

Add behaviour-chaining and you get a powerful tool for devoloping
seemingly complicated character behaviours.

Good ol' OO.

.ed

Andrew Plotkin

unread,
Apr 8, 2003, 4:21:51 PM4/8/03
to
Here, Edmund Kirwan <ade...@eircom.net> wrote:

> Ok, the player-coin thing was just an example, and it shouldn't be
> enlarged upon ... but what the hell.

Heh. It may turn into an interesting illustration, however.

> [...]


> There will also be Behaviour objects (actually, they're classes). And
> one subclass of this Behaviour class will be a CheckWhatIHaveBehaviour
> class. And each Behaviour class can be have an associated Consequence
> class. And one subclass of this Consequence class will be a
> ShoutConsequence.

> And to glue it all together, the Player class can have as many
> associated Behaviour classes as it likes. So the startup will look
> like:

> coin = new Coin;
> player = new Player;
> shoutConsequence = new ShoutConsequence("Wow, I'm rich!");
> haveCoin = new CheckWhatIHaveBehaviour();
> haveCoin.add(coin);
> haveCoin.addConsequence(shoutConsequence);
> player.addBehaviour(haveCoin);

Hm. You can say that you're rising above the "property of X or
property of Y" debate, but that sure looks like a property of the
player. :)

But this is *very roughly* the equivalent of an Inform "after"
property -- you're adding a non-state-changing behavior to an existing
state change of the model world.

Life gets more interesting when you add state-changing customizations.
(The rough equivalent of "before" properties.) The coin is too hot to
touch, so you don't pick it up. Your hands are full, so you don't pick
it up. From a practical standpoint -- and I mean the author's
practicality -- the former should be a property of the coin, and the
latter a property of the player.

(The fact that you're representing this as code constructed on the
fly, whereas Inform has static literal data structures for quite a
lot of straightforward cases, is a separate issue. Just to get that
out of the way...)

--Z

"And Aholibamah bare Jeush, and Jaalam, and Korah: these were the borogoves..."
*
* Make your vote count. Get your vote counted.

L. Ross Raszewski

unread,
Apr 8, 2003, 8:17:24 PM4/8/03
to
On 8 Apr 2003 12:55:02 -0700, Edmund Kirwan <ade...@eircom.net> wrote:
>To overcome this, "Irritating asymmetry," you've to think far more OO
>than this.
>
>The answer to, "Which of the 2 or 3 objects ... owns the apt method,"
>is not one of them. The object that will own the special logic will be
>the object that's an instance of the player checking his pockets and
>finding a coin.

This is one of the limitations of OO (Yes, folks, OO is not the end
all and be all of programming languages, I don't care what your Intro
to Java teacher said.); the method has to belong to *one of them*.
The transfer of the coin to the player is not "something about the
coin" or "something about the player". It's something else
entirely. By the same token, driving is not something 'about' my car:
my car is something which engages in driving. In an OO paradigm, my
car is drivable because it inherits a drive method from the class of
things that can be driven. But that's not the way the language works:
driving is not a property of certain kinds of object, it's an action,
which has existance outside of the realm of objects, in which certain
kinds of object can participate (And likewise other extremes; in
Smalltalk, the statement '3 + 4' literally means 'Three, add thyself
to Four': addition is "something about" 3.). In the OO paradigm,
actions only exist as "something about" an object, but this doesn't
reflect the way the world really works. (Note that I'm not saying that
OO is bad or unuseful. I'm only saying that it is a tool that applies
to a particular set of problems, not to all of them. the IF input
cycle is really verb-driven, not object-driven, and most of the
sorts of things one wants to do are imperative, not
objective. Hm. Functional IF languages. There's a scary thought.(Yes,
I know that Lisp is functional and Zil is a lisp derivative. But what
I've read of Zil makes it look a lot more like an imperative language
that simply uses lisp-like syntax)).

Mike Roberts

unread,
Apr 8, 2003, 9:10:03 PM4/8/03
to
"L. Ross Raszewski" <lrasz...@loyola.edu> wrote:
> This is one of the limitations of OO [...]; the method has to
> belong to *one of them*. [...] In an OO paradigm, my car

> is drivable because it inherits a drive method from the class
> of things that can be driven. But that's not the way the
> language works: driving is not a property of certain kinds
> of object, it's an action, which has existance outside of the
> realm of objects, in which certain kinds of object can
> participate.

I'm not sure this is a limitation of OO design so much as a particular
choice of object model. If a "drive" action needs to be represented
separately from the thing being driven or the person doing the driving, so
be it: create an object to represent the "drive" action, and put whatever
code and data define the action on that action object. The protocol would
presumably be something like this: a driver wants to drive something, so the
driver creates a "drive action" object, specifying the parameters of the
action; the driver might then call additional methods on the drive action
object to carry out the action; the drive action object operates on the
thing being driven by calling a separate interface on the thing being
driven. (This might seem like pointless indirection, in that it might sound
like we still need a "drive" interface on the car, but in fact the entire
abstraction of driving could be handled in the drive action object, with the
car exposing only a low-level interface that deals in terms like
setVelocityVector and setPosition and the like.)

This really isn't so different from the IF formulation, when you look at it
closely. There is indeed a "verb" object representing the action; in Inform
and tads 2 it's more or less just an enumerated identifier, but it could
still be viewed as a degenerate object. In tads 3, the action object is
considerably more elaborated, and actually takes on a coordination role in
carrying out the command.

It's been debated here before whether it's better to put the entire code for
an action on the objects involved or in the verb. The latter approach is
still OO - the individual actions would be subclasses of a base generic
action, which would define the interface the parser uses to carry out a
command. My experience is that putting the code on the objects rather than
on the actions is a much better fit for IF as we know it, because we have a
very coarse physical model and therefore need to virtualize high-level
operations (at the level of the actions the player enters): we need to
override "put in" for containers because you can't derive the ability of one
object to contain another from anything at a lower level in the physical
model. A highly simulationist model would go the opposite direction,
though: you'd define each high-level (player) action as a series of
low-level "physics" operations on the world model, so each object would be
defined entirely by its physical properties, not by special-case code.
Containment, for example, would be a natural consequence of physical
enclosure that's modeled geometrically.

Dan Schmidt

unread,
Apr 9, 2003, 10:38:35 AM4/9/03
to
lrasz...@loyola.edu (L. Ross Raszewski) writes:

| This is one of the limitations of OO (Yes, folks, OO is not the end
| all and be all of programming languages, I don't care what your
| Intro to Java teacher said.); the method has to belong to *one of
| them*.

Unless your language supports multimethods, like Common Lisp or Dylan.

--
http://www.dfan.org

David A. Cornelson

unread,
Apr 9, 2003, 12:06:42 PM4/9/03
to
"Dan Schmidt" <df...@dfan.org> wrote in message
news:uhe97d...@dfan.org...

What are multimethods? Could you explain in more detail and maybe give us an
example, even in Lisp?

Dave


Dan Schmidt

unread,
Apr 9, 2003, 1:47:45 PM4/9/03
to

With regular methods in OO, you can only polymorphically dispatch on
one object at a time. Polymorphism happens because classes A and B
both define a method foo, and obj.foo calls A::foo or B::foo depending
on what class obj is.

With multimethods, methods are free-floating functions, rather than
belonging to a single class. You don't say obj.foo(), you say
foo(obj), but you might have already written a function foo that takes
an A, and a function foo that takes a B.

The powerful thing is that functions can take more than one argument,
so you're not limited to dispatching based on only one object. If C
and D are more classes, you could have functions

foo (A, C)
foo (A, D)
foo (B, C)
foo (B, D)

and a call to foo would dispatch based on the types of both arguments.
In C++ you'd have to dispatch one step at a time, which is awkward and
often not really semantically what you mean anyway.

The canonical example is (in pseudo-code):

class Rock
class Paper
class Scissors

beats (Paper obj1, Rock obj2) = true
beats (Scissors obj1, Paper obj2) = true
beats (Rock obj1, Scissors obj2) = true
beats (Object obj1, Object obj2) = false // default case

which is a pain to do in something like C++ (using classes).

--
http://www.dfan.org

David A. Cornelson

unread,
Apr 9, 2003, 2:49:51 PM4/9/03
to
"Dan Schmidt" <df...@dfan.org> wrote in message
news:uwui3b...@dfan.org...

>
> The canonical example is (in pseudo-code):
>
> class Rock
> class Paper
> class Scissors
>
> beats (Paper obj1, Rock obj2) = true
> beats (Scissors obj1, Paper obj2) = true
> beats (Rock obj1, Scissors obj2) = true
> beats (Object obj1, Object obj2) = false // default case
>
> which is a pain to do in something like C++ (using classes).
>

Okay. How is this different than using overloads and some service class? Of
course not all languages support overloads, but that's a separate issue.

class RockScissorsPaper
{
public bool beats (Paper obj1, Rock obj2) {...} // returns true
public bool beats (Scissors obj1, Paper obj2) {...} // returns false
public bool beats (Rock obj1, Scissors obj2) {...} // returns true
public bool beats (Object obj1, Object obj2) {...} // default
case
}

Dave


John W. Kennedy

unread,
Apr 9, 2003, 2:56:28 PM4/9/03
to
David A. Cornelson wrote:
> "Dan Schmidt" <df...@dfan.org> wrote in message
> news:uwui3b...@dfan.org...
>
>>The canonical example is (in pseudo-code):
>>
>>class Rock
>>class Paper
>>class Scissors
>>
>>beats (Paper obj1, Rock obj2) = true
>>beats (Scissors obj1, Paper obj2) = true
>>beats (Rock obj1, Scissors obj2) = true
>>beats (Object obj1, Object obj2) = false // default case
>>
>>which is a pain to do in something like C++ (using classes).
>>
>
>
> Okay. How is this different than using overloads and some service class? Of
> course not all languages support overloads, but that's a separate issue.

Overloads are compile-time.

--
John W. Kennedy
"Only an idiot fights a war on two fronts. Only
the heir to the throne of the kingdom of idiots
would fight a war on twelve fronts."
-- "Babylon 5"

Daniel Barkalow

unread,
Apr 9, 2003, 3:09:22 PM4/9/03
to
On Tue, 8 Apr 2003, Cedric Knight wrote:

> I've not been a great fan of object-orientation in general languages,
> but would say that (a) encapsulation (which I thought originally meant
> binding code to data structures), and (b) the hiding of private
> properties which encapsulation enables, are very useful but not
> essential in the IF model world.

I don't consider encapsulation to necessarily bind code to data; it could
involve binding a section of code together to hide implementation details
that don't involve data at all. Of course, code is, in some sense, data,
and code that doesn't hold any data at all is generally not
interesting. But (for example) implementing a number of related functions
in a single file, such that nothing else depends on how those functions
are implemented, is a form of encapsulation.

> In more simulationist IF, it would be good to have
> actions regulated by a single method unique to an object or class of
> objects (TADS seems better in this respect than Inform). There is an
> irritating asymmetry here, though: should the programmer use
> 'player.receive(coin)' or 'coin.insertin(player)'? Which of the 2 or 3
> objects involved in a complex action owns the appropriate method and how
> does the programmer remember this? It is likely that any <Transfer coin
> player> action will have to check both a method in the coin and the
> player, but what is the hierarchy of this procedure; which is the
> fully-validated method, and which is the primitive?

The answer that's best for IF is probably that the primitive is not on an
object, but part of the library, and is only called after all objects
involved have gotten a crack at the action. I.e., "move noun to second" is
in TransferSub. Each object can stop the action or cause something to
happen (where the thing they cause to happen is probably an action, which
goes back through the whole process), but it ends up falling to the
library code for managing the containment relationship to move objects in
the tree if this is the effect that the objects agree on.

In this case, there are a set of library functions for handling the
locations of objects, and these may be overridden by objects, but when an
object changes location, it must do so using the library functions, either
by not preventing the action in progress or by starting a different
action instead or in addition.

Note that, in IF, the delegation even goes beyond multiple-object
dispatch; objects not part of the call but in scope also get a chance to
affect the results.

> Without a strict convention and lots of forethought here, the player
> object is going to have to be declared a 'friend' to the coin object,
> and vice versa, and you might as well not have private properties at
> all.

I think that aspects which you can't assign entirely to either must be
assigned to "the model world". Of course, the model world might be storing
data on the objects for convenience, but that's an implementation detail.

> A special case in IF is setting up the initial state of the world. The
> usual way of doing this in Inform is directly accessing all properties
> so that they could in contain non-validated data; but bugs produced this
> way are easy to trace, and it's not *substantially* different from a
> more OO way of doing things, like:

In principle, the compiler could generate calls to arrange the objects in
the way they specify; in fact, it could generate the calls, run them, and
then output the resulting state as the game file. But the information
about where an object begins the game should usually be communicated by
the programmer to the system in the object declaration.

Cedric Knight

unread,
Apr 9, 2003, 3:43:25 PM4/9/03
to

"Edmund Kirwan" <ade...@eircom.net> wrote
> "Cedric Knight" <ckn...@gn.babpbc.removeallBstosend.org> wrote

> > In more simulationist IF, it would be good to have
> > actions regulated by a single method unique to an object or class of
> > objects (TADS seems better in this respect than Inform). There is
an
> > irritating asymmetry here, though: should the programmer use
> > 'player.receive(coin)' or 'coin.insertin(player)'? Which of the 2
or 3
> > objects involved in a complex action owns the appropriate method and
how
> > does the programmer remember this?

>


> To overcome this, "Irritating asymmetry," you've to think far more OO
> than this.
>
> The answer to, "Which of the 2 or 3 objects ... owns the apt method,"
> is not one of them. The object that will own the special logic will be
> the object that's an instance of the player checking his pockets and
> finding a coin.

That's dealt with the aesthetic problem, but not all the problems in
getting encapsulation and private properties working. We now have
Take.attempt(player,coin)

The 'Take' now performs a marriage service, asking player 'do you take
this coin to be your child?', likewise 'do you, coin, take player to be
your container object?', in a polite, object-oriented way.

If neither object raises an objection, what happens next? In order to
change the world state, does the Take code directly access what we
wanted to be a private property of the coin, or call a
PostvalidatedPerformMove() method? I guess TADS 3 does something like
the second, and Mike Roberts is further on in solving all these problems
than most of us.

What's troubling me is the following situation. The coin is happily
being guarded by a boatmaptroll and the way to get it is to cast a
fnootz levitation spell. Casting the spell can't call
Take.attempt(player,coin) because the coin would still object to it. So
the Fnootz code has to bypass the preconditions and go to the
actionTake(player,coin) stage.

Seems to work fine, but the puzzle solution forgets to check the
inventory capacity of the player, which was I started by hoping
encapsulation would force to happen. We have more encapsulation, but at
no great benefit; Take (presumably in the library) and Fnootz
(presumably written by the IF author) are both allowed to alter the
status of the coin without validation.

I hope this makes some kind of sense. I'm pretty sure it's one reason
why, as Kevin put it, neither Inform nor TADS takes the black-box
approach of enforcing a limited interface to object data, and why OO in
IF is no substitute for programmer discipline (of which, alas, I have
none).

CK


Mike Roberts

unread,
Apr 9, 2003, 4:16:06 PM4/9/03
to
"David A. Cornelson" <david dot cornelson at iflibrary dot com> wrote:
> "Dan Schmidt" <df...@dfan.org> wrote in message
> [about "multimethods" in dylan and common lisp]
> > beats (Paper obj1, Rock obj2) = true [etc]

>
> Okay. How is this different than using overloads and some service
> class? Of course not all languages support overloads, but that's a
> separate issue.
>
> class RockScissorsPaper
> {
> public bool beats (Paper obj1, Rock obj2) {...} // returns true
> public bool beats (Scissors obj1, Paper obj2) {...} // returns
false
> public bool beats (Rock obj1, Scissors obj2) {...} // returns true
> public bool beats (Object obj1, Object obj2) {...} // default
> }

Give it a try with a program like this:

Object *obj1 = new Paper();
Object *obj2 = new Rock();
if (beats(*obj1, *obj2)) printf("yes"); else printf("no");

This'll print "no" even though beats(Paper, Rock) should return true. The
problem is that C++ overloading is bound at compile time: the compiler binds
the beats() call here to the one with the (Object,Object) signature, because
all the compiler knows about the situation is that you have two Object
arguments. The multimethod mechanism that Dan describes must be run-time
bound.

In Java, you could implement an equivalent mechanism by using reflection,
but it would be very high overhead. I don't think C++'s RTTI features are
strong enough to make such a thing possible in general, although you could
hack it for particular cases by using a switch() on the run-time types.

David A. Cornelson

unread,
Apr 9, 2003, 6:15:39 PM4/9/03
to
"Mike Roberts" <mjrUND...@hotmail.com> wrote in message
news:2h%ka.12$PD5...@news.oracle.com...

Ah....so I would have to have _one_ beats method that handled all the logic,
which defeats the whole purpose...

public static beats(...)
{
if (obj1 of Rock && obj2 of Paper) return false;
if (obj1 of Paper && obj2 of Rock) return true;
...
}

This right?

Dave


Dan Schmidt

unread,
Apr 9, 2003, 6:22:57 PM4/9/03
to
"David A. Cornelson" <david dot cornelson at iflibrary dot com> writes:

| "Mike Roberts" <mjrUND...@hotmail.com> wrote in message
| news:2h%ka.12$PD5...@news.oracle.com...

|| In Java, you could implement an equivalent mechanism by using reflection,
|| but it would be very high overhead. I don't think C++'s RTTI features are
|| strong enough to make such a thing possible in general, although you could
|| hack it for particular cases by using a switch() on the run-time types.
||
|
| Ah....so I would have to have _one_ beats method that handled all the logic,
| which defeats the whole purpose...
|
| public static beats(...)
| {
| if (obj1 of Rock && obj2 of Paper) return false;
| if (obj1 of Paper && obj2 of Rock) return true;
| ...
| }
|
| This right?

Yes, plus now you're basically doing explicit switching based on
class, which is the big thing that polymorphism and OO are supposed
to allow you not to do.

--
http://www.dfan.org

David A. Cornelson

unread,
Apr 9, 2003, 10:03:24 PM4/9/03
to
"Dan Schmidt" <df...@dfan.org> wrote in message
news:u8yujb...@dfan.org...

Okay - to complete the understanding of this....and I think we're way off
topic, but this certainly interesting...

I tried like hell to implement the Rock/Paper/Scissors logic in C# and was
oh so close...but no cigar...

I ran into numerous problems tyring to use user-defined type converters,
attributes, but I finally realized that (I think) the only way to do this is
using function templates in c++.

At least that's what I think I figure out.

But I don't know enough about attribute programming to say this is
impossible.

Dave


Horpner

unread,
Apr 9, 2003, 10:54:39 PM4/9/03
to

"David A. Cornelson" <david dot cornelson at iflibrary dot com> wrote in
message news:AfSdnb2gYbp...@speakeasy.net...

> Ah....so I would have to have _one_ beats method that handled all the
logic,
> which defeats the whole purpose...
>
> public static beats(...)
> {
> if (obj1 of Rock && obj2 of Paper) return false;
> if (obj1 of Paper && obj2 of Rock) return true;
> ...
> }
>
> This right?

Correct. The cool thing about multiple dispatch is that I can add new
methods to existing classes without modifying them. For example, I could
add symbols to the Rock, Scissors and Paper game, developing
new rules for which beats which as I go. For example, I could easily
create a Rock, Paper, Scissors, Stiffy game.

If y'all use C++, check out "The Visitor Pattern" to see how double
dispatch is normally accomplished in C++; this pattern may apply in
all OO languages that provide only single dispatch.

Of course, if what I say is true, then Inform, for example, can already
do this. Hmmm.

Neil C.


David A. Cornelson

unread,
Apr 10, 2003, 12:21:08 AM4/10/03
to
"Mike Roberts" <mjrUND...@hotmail.com> wrote in message
news:2h%ka.12$PD5...@news.oracle.com...

I tried again and got it to work, but I had to cast the three objects as
their own type instead of as Object.

Why does that matter?

Here's the code (C#):

using System;

namespace RockPaperScissors
{
class RPS
{
[STAThread]
static void Main(string[] args)
{
Paper paper = new Paper();
Rock rock = new Rock();
Scissors scissors = new Scissors();

Console.Write("Rock beats Scissors: " + serviceRPS.beats(rock,scissors));
Console.ReadLine();
}
}

public class Rock
{
public Rock()
{
}
}

public class Paper
{
public Paper()
{
}
}

public class Scissors
{
public Scissors()
{
}
}

public class serviceRPS
{
public static int beats(Rock obj1, Paper obj2)
{
return -1;
}
public static int beats(Rock obj1, Scissors obj2)
{
return 1;
}
public static int beats(Rock obj1, Rock obj2)
{
return 0;
}
public static int beats(Paper obj1, Paper obj2)
{
return 0;
}
public static int beats(Paper obj1, Rock obj2)
{
return 1;
}
public static int beats(Paper obj1, Scissors obj2)
{
return -1;
}
public static int beats(Scissors obj1, Scissors obj2)
{
return 0;
}
public static int beats(Scissors obj1, Paper obj2)
{
return 1;
}
public static int beats(Scissors obj1, Rock obj2)
{
return -1;
}
public static int beats(Object obj1, Object obj2)
{
return -2;
}
}
}


Mike Roberts

unread,
Apr 10, 2003, 12:40:41 AM4/10/03
to
"David A. Cornelson" <david dot cornelson at iflibrary dot com> wrote:
[about "multimethods" in dylan and common lisp, vs. C++ overloading]

> > Give it a try with a program like this:
> >
> > Object *obj1 = new Paper();
> > Object *obj2 = new Rock();
> > if (beats(*obj1, *obj2)) printf("yes"); else printf("no");
> >
> I tried again and got it to work, but I had to cast the three objects as
> their own type instead of as Object.
>
> Why does that matter?

Which "why does that matter" do you mean, "why does that make it work?" or
"why is that undesirable?"? :)

Why it makes it work: because of the compile-time binding. If you tell the
compiler up front, using a type-cast, the final concrete class of each
argument, then you're telling the compiler enough to let it pick the
overloaded method with the signature that includes the final concrete types.

Why it's undesirable: because of the compile-time binding. If you know at
compile time that you have a Paper and a Rock, then there's not much need to
write the beats() expression in the first place, since you can tell just
looking at the source code what beats() is going to return. But you
presumably want to be able to write code where you *don't* know at
compile-time the final class of the objects - something like this:

void myMethod(Object *obj1, Object *obj2)
{
if (beats(*obj1, *obj2)) ...
}

In other words, you probably want to be able to write code where the objects
come in from points unknown, so you don't know in advance whether obj1 is a
Rock, Paper, or Scissors. Of course, you could write the four overloaded
versions of myMethod with the same four signatures that we have for beats(),
which would allow each version of myMethod() to call the correct version of
beats() - but this obviously just pushes the problem up a level, passing the
problem along to every caller of myMethod(). Plus, you have four times the
myMethod's, all identical except for type signatures.

Dan Schmidt

unread,
Apr 10, 2003, 12:20:22 AM4/10/03
to
"David A. Cornelson" <david dot cornelson at iflibrary dot com> writes:

| I ran into numerous problems tyring to use user-defined type
| converters, attributes, but I finally realized that (I think) the
| only way to do this is using function templates in c++.

Nope, templates are also a compile-time mechanism.

I think the only way to do this in C++ without RTTI is 'double
dispatch': explicitly performing polymorphic dispatch on one object
and then the other. But you end up with n^2 methods that are
scattered all over the place.

Sample code (untested):

class Object
{
virtual bool Beats( const Object& obj ) const = 0;
virtual bool IsBeatenByRock() const = 0;
virtual bool IsBeatenByPaper() const = 0;
virtual bool IsBeatenByScissors() const = 0;

};

class Rock : public Object
{
bool Beats( const Object& obj ) const { obj.IsBeatenByRock() }

bool IsBeatenByRock() const { return false; }
bool IsBeatenByPaper() const { return true; }
bool IsBeatenByScissors() const { return false; }
};

class Paper : public Object
{
bool Beats( const Object& obj ) const { obj.IsBeatenByPaper() }

bool IsBeatenByRock() const { return false; }
bool IsBeatenByPaper() const { return false; }
bool IsBeatenByScissors() const { return true; }
};

class Scissors : public Object
{
bool Beats( const Object& obj ) const { obj.IsBeatenByScissors() }

bool IsBeatenByRock() const { return true; }
bool IsBeatenByPaper() const { return false; }
bool IsBeatenByScissors() const { return false; }
};

Dan

--
http://www.dfan.org

Daniel Barkalow

unread,
Apr 10, 2003, 6:32:41 PM4/10/03
to
On Wed, 9 Apr 2003, Cedric Knight wrote:

> That's dealt with the aesthetic problem, but not all the problems in
> getting encapsulation and private properties working. We now have
> Take.attempt(player,coin)
>
> The 'Take' now performs a marriage service, asking player 'do you take
> this coin to be your child?', likewise 'do you, coin, take player to be
> your container object?', in a polite, object-oriented way.

This action should probably be something more like "MoveTo"; since it
logically applies to at least some aspects of, for example, putting an
object in a bag or on a desk. "Take" is something players do which
normally results in a MoveTo if nothing goes wrong.

> If neither object raises an objection, what happens next? In order to
> change the world state, does the Take code directly access what we
> wanted to be a private property of the coin, or call a
> PostvalidatedPerformMove() method? I guess TADS 3 does something like
> the second, and Mike Roberts is further on in solving all these problems
> than most of us.

Since the relation is between the container and the contained object,
neither could have the information as private data. What makes sense it to
encapsulate the information in a set of library functions instead. These
functions hide the implementation of the containment relation and provide
the logical operations on it: find the location of an object, find the
contents of a container, and move an object.

> What's troubling me is the following situation. The coin is happily
> being guarded by a boatmaptroll and the way to get it is to cast a
> fnootz levitation spell. Casting the spell can't call
> Take.attempt(player,coin) because the coin would still object to it. So
> the Fnootz code has to bypass the preconditions and go to the
> actionTake(player,coin) stage.

Obviously, the spell is not an ordinary "Take" action (since you could
just use "take" instead, then). Instead, there's a Fnootz action
(prevented by anti-magic effects, presumably). This, like "Take", ends up
calling MoveTo.

> Seems to work fine, but the puzzle solution forgets to check the
> inventory capacity of the player, which was I started by hoping
> encapsulation would force to happen.

The inventory capacity is a limitation on MoveTo, not on Take, and will be
enforced when the library attempts to actually move the coin.

> I hope this makes some kind of sense. I'm pretty sure it's one reason
> why, as Kevin put it, neither Inform nor TADS takes the black-box
> approach of enforcing a limited interface to object data, and why OO in
> IF is no substitute for programmer discipline (of which, alas, I have
> none).

I suspect neither language does this because you end up with a whole lot
of actions getting run for each command from the user, which could get
slow. It's also a rather complicated solution.

Seebs

unread,
Apr 11, 2003, 2:11:26 AM4/11/03
to
In article <b72mde$adm0k$1...@ID-60390.news.dfncis.de>,

Horpner <cer...@trans-video.net> wrote:
>Correct. The cool thing about multiple dispatch is that I can add new
>methods to existing classes without modifying them. For example, I could
>add symbols to the Rock, Scissors and Paper game, developing
>new rules for which beats which as I go. For example, I could easily
>create a Rock, Paper, Scissors, Stiffy game.

A friend of mine used to make everyone play rock paper scissors all the time.
Eventually, someone pointed a finger at her and said "Gun. Gun beats
everything." She used this whenever she got frustrated, and the third time
she tried it on me, I splayed my fingers out and said "Space alien is immune
to gun". Of course, I knew she'd have to try the new thing, so I did Rock
next, and said "Townspeople throw rocks at space alien". We also had rules
for "little bunny foo-foo" (make bunny ears, make hopping motion), but I
forget what he beat.

-s
--
Copyright 2002, all wrongs reversed. Peter Seebach / se...@plethora.net
$ chmod a+x /bin/laden Please do not feed or harbor the terrorists.
C/Unix wizard, Pro-commerce radical, Spam fighter. Boycott Spamazon!
Consulting, computers, web hosting, and shell access: http://www.plethora.net/

dgr...@cs.csbuak.edu

unread,
Apr 11, 2003, 11:08:04 AM4/11/03
to
Seebs <se...@plethora.net> wrote:
> In article <b72mde$adm0k$1...@ID-60390.news.dfncis.de>,
> Horpner <cer...@trans-video.net> wrote:
>>Correct. The cool thing about multiple dispatch is that I can add new
>>methods to existing classes without modifying them. For example, I could
>>add symbols to the Rock, Scissors and Paper game, developing
>>new rules for which beats which as I go. For example, I could easily
>>create a Rock, Paper, Scissors, Stiffy game.

> A friend of mine used to make everyone play rock paper scissors all the time.
> Eventually, someone pointed a finger at her and said "Gun. Gun beats
> everything." She used this whenever she got frustrated, and the third time
> she tried it on me, I splayed my fingers out and said "Space alien is immune
> to gun". Of course, I knew she'd have to try the new thing, so I did Rock
> next, and said "Townspeople throw rocks at space alien". We also had rules
> for "little bunny foo-foo" (make bunny ears, make hopping motion), but I
> forget what he beat.

Where was this? I remember in elementary school coming up with similar
variants. Except, I think "space alien" was made with two fingers curled
back as if throwing a knuckle-ball and the splayed out fingers was
"Cthulhu".


--
David Griffith

Andrew Plotkin

unread,
Apr 11, 2003, 11:38:43 AM4/11/03
to
Here, Seebs <se...@plethora.net> wrote:

> A friend of mine used to make everyone play rock paper scissors all the time.
> Eventually, someone pointed a finger at her and said "Gun. Gun beats
> everything." She used this whenever she got frustrated, and the third time
> she tried it on me, I splayed my fingers out and said "Space alien is immune
> to gun". Of course, I knew she'd have to try the new thing, so I did Rock
> next, and said "Townspeople throw rocks at space alien". We also had rules
> for "little bunny foo-foo" (make bunny ears, make hopping motion), but I
> forget what he beat.

The heads of field mice. Duh.

Edmund Kirwan

unread,
Apr 11, 2003, 3:16:25 PM4/11/03
to
Daniel Barkalow <iabe...@iabervon.org> wrote in message news:<Pine.LNX.4.21.03041...@iabervon.org>...

> On Wed, 9 Apr 2003, Cedric Knight wrote:
>
>
> > What's troubling me is the following situation. The coin is happily
> > being guarded by a boatmaptroll and the way to get it is to cast a
> > fnootz levitation spell. Casting the spell can't call
> > Take.attempt(player,coin) because the coin would still object to it. So
> > the Fnootz code has to bypass the preconditions and go to the
> > actionTake(player,coin) stage.
>
> Obviously, the spell is not an ordinary "Take" action (since you could
> just use "take" instead, then). Instead, there's a Fnootz action
> (prevented by anti-magic effects, presumably). This, like "Take", ends up
> calling MoveTo.
>
> > Seems to work fine, but the puzzle solution forgets to check the
> > inventory capacity of the player, which was I started by hoping
> > encapsulation would force to happen.
>
> The inventory capacity is a limitation on MoveTo, not on Take, and will be
> enforced when the library attempts to actually move the coin.
>
> I suspect neither language does this because you end up with a whole lot
> of actions getting run for each command from the user, which could get
> slow. It's also a rather complicated solution.

Interesting that you bring up that this, "Could get slow." Isn't IF a
peculiarly simple and not-very-computer-intensive strain of game?
Surely, a machine could process a dozen verbs with world-warping
consequences and spit out half a book of prose in the time it takes to
draw even Lara Croft's <insert anatomical fave here>?

I say this because I had a go at just the sort of system you're
hinting at, one in which the Verbs are reduced to mechanical minimum,
and after each verb the entire space of items are churned through to
see if any item requires special servicing.

This doesn't solve the untakable-coin problem, but views it from a
different perspective. From this perspective, the Take verb will allow
you take the coin, no matter that a troll is guarding it. All verbs
will be allowed to perform if the fundamentals are addressed: you can
wear the coat, as long as you're carrying it and not already wearing
it. You can drink the water, as long as there is any water left to
drink. Etc.

Then, when the Take is completed, the processor shunts through all
items, and finds one bahaviour that you've assigned at start-up: if
anyone has the coin while the troll is still alive, then the troll
will attack that person.

Just so, you can always give the gun to the prisoner. There will be no
logic in the Give verb that will check whether this is a gun, and
whether the recipient is a prisoner, and if so, the prisoner goes
trigger-happy. Instead, the Give logic checks that the recipient is in
a state to receive stuff (i.e., is alive), and that the giver really
possesses what he wants to give.

It's in another behaviour, specified at start-up, something like: if
prisoner has gun, start shooting.

This encapsulates Verb actions from their consequences, and thus the
programmer doesn't have to think of how the prisoner attains the gun:
whether he takes it, is given it, or it's magically teleported into
his lap by a monkey. The behaviour only cares that he has it.

And it's no so slow at all (not on this mere 600mHz/128m 2-year-old in
front of me). Response time's < 0.5 seconds with around 400 items.

.ed


/==================\
www.edmundkirwan.com
"It's not very good."

Edmund Kirwan

unread,
Apr 11, 2003, 3:39:55 PM4/11/03
to
"Mike Roberts" <mj...@hotmail.com> wrote in message news:<dB6la.171$da5...@newssvr19.news.prodigy.com>...

>
> In other words, you probably want to be able to write code where the objects
> come in from points unknown, so you don't know in advance whether obj1 is a
> Rock, Paper, or Scissors. Of course, you could write the four overloaded
> versions of myMethod with the same four signatures that we have for beats(),
> which would allow each version of myMethod() to call the correct version of
> beats() - but this obviously just pushes the problem up a level, passing the
> problem along to every caller of myMethod(). Plus, you have four times the
> myMethod's, all identical except for type signatures.
>
> --Mike
> mjr underscore at hotmail dot com

Oh, what the hell, we're so far off track we'll probably end up on
track again if we keep going ... (and I won't mention the J-word...)

Class RPSThing() {
boolean doIbeatYou(Rock rock) {
return false;
}

boolean doIBeatYou(Paper paper) {
return false;
}

boolean doIBeatYou(Scissors scissors) {
return false;
}
}
------

Class Rock extends RPSThing {
boolean doIBeatYou(Paper paper) {
return true;
}
}
------

Class Paper extends RPSThing {
boolean doIBeatYou(Scissors scissors) {
return true;
}
------

Class Scissors extends RPSThing {
boolean doIBeatYou(Rock rock) {
return true;
}
}
------

Class Game {
...

void play(RPSThing thing1, RPSThing thing2) {
if (thing1.doIBeatYou(thing2) {
System.out.println(thing1 + " wins");
} else {
System.out.println(thing2 + " wins");

Neil Cerutti

unread,
Apr 11, 2003, 4:08:49 PM4/11/03
to
In article <a80e1059.03041...@posting.google.com>,

Edmund Kirwan wrote:
> Daniel Barkalow <iabe...@iabervon.org> wrote in message
> news:<Pine.LNX.4.21.03041...@iabervon.org>...
>> I suspect neither language does this because you end up with a
>> whole lot of actions getting run for each command from the
>> user, which could get slow. It's also a rather complicated
>> solution.
>
> Interesting that you bring up that this, "Could get slow."
> Isn't IF a peculiarly simple and not-very-computer-intensive
> strain of game? Surely, a machine could process a dozen verbs
> with world-warping consequences and spit out half a book of
> prose in the time it takes to draw even Lara Croft's <insert
> anatomical fave here>?

And thus, users expect to be able to run such games on minimalist
and archaic hardware. 486, 386, 286, Amiga 500, Palm II, Mobil
Phone, Wrist-watch, etc...

True story: The initial release of _Anchorhead_ was too slow to
be usable for a lot of people (it turned out to be caused by
Inform's inefficient compilation of objectloops, which can be
worked around).

Usually, though, it is complications in parsing that cause a game
to bog down, not action processing, as discussed. After all,
action processing is generally linear in complexity, while the
natural language parsing process is probably quadratic.

So in the end, I wouldn't blink about adding a bunch of overhead
to action processing. But in general I *would* worry about
efficiency.

--
Neil Cerutti

Mike Roberts

unread,
Apr 11, 2003, 4:10:43 PM4/11/03
to
"Edmund Kirwan" <ade...@eircom.net> wrote:
[about dynamic overloading]

> Oh, what the hell, we're so far off track we'll probably end
> up on track again if we keep going ... (and I won't mention
> the J-word...)
>
> Class RPSThing() {
> boolean doIbeatYou(Rock rock) { return false; }
> boolean doIBeatYou(Paper paper) { return false; }
> boolean doIBeatYou(Scissors scissors) { return false; }
> }
> Class Rock extends RPSThing {
> boolean doIBeatYou(Paper paper) { return true; }
> // etc for other classes

>
> void play(RPSThing thing1, RPSThing thing2) {
> if (thing1.doIBeatYou(thing2) {
> System.out.println(thing1 + " wins");
> } else {
> System.out.println(thing2 + " wins");
> }
> }

Try it in Java or C++, and you'll find it won't work. Two problems. First,
you'll get a compiler error on the call to thing1.doIBeatYou(thing2),
because you haven't defined an RPSThing method with the signature
doIBeatYou(RPSThing) - you've defined doIBeatYou(Rock), doIBeatYou(Paper),
and doIBeatYou(Scissors), but you can see looking at your play() code that
you haven't called any of those. You've called doIBeatYou(RPSThing), which
isn't defined. Okay, trivial problem, right? Just add

boolean doIBeatYou(RPSThing) { return false; }

to RPSThing, and you're done, right? Well, now we come to problem #2: your
code will always say "<thing2> wins", because thing1.doIBeatYou(thing2) will
always return false. It always returns false because the compiler binds to
the overloaded doIBeatYou with the (RPSThing) signature, and that version
always returns false.

M. David Krauss

unread,
Apr 11, 2003, 4:36:22 PM4/11/03
to
On Fri, 11 Apr 2003 15:38:43 +0000 (UTC)
Andrew Plotkin <erky...@eblong.com> wrote:

> Here, Seebs <se...@plethora.net> wrote:
>
> > A friend of mine used to make everyone play
> > rock paper scissors all the time. Eventually,
> > someone pointed a finger at her and said "Gun.
> > Gun beats
> > everything." She used this whenever she got
> > frustrated, and the third time she tried it on
> > me, I splayed my fingers out and said "Space
> > alien is immune to gun". Of course, I knew
> > she'd have to try the new thing, so I did Rock
> > next, and said "Townspeople throw rocks at
> > space alien". We also had rules for "little
> > bunny foo-foo" (make bunny ears, make hopping
> > motion), but I forget what he beat.
>
> The heads of field mice. Duh.
>

And of course the Good Fairy beats Little Bunny
Foo-Foo, but what on Earth or under it do the
Heads of Field Mice beat?

-Matthew

(Is the Good Fairy carrying a "Foo-Bar"?)

Kevin Forchione

unread,
Apr 11, 2003, 4:40:09 PM4/11/03
to
"Mike Roberts" <mjrUND...@hotmail.com> wrote in message
news:%nFla.17$037...@news.oracle.com...

Bringing this back down to an IF language, what would be the best way to
model this behavior in TADS 3?

--Kevin


Adam Thornton

unread,
Apr 11, 2003, 5:10:41 PM4/11/03
to
In article <20030411163622.7...@wiredfrog.com>,

M. David Krauss <webm...@wiredfrog.com> wrote:
>And of course the Good Fairy beats Little Bunny
>Foo-Foo, but what on Earth or under it do the
>Heads of Field Mice beat?

The Feet of Elephants.

Mike Roberts

unread,
Apr 11, 2003, 5:44:12 PM4/11/03
to
"Kevin Forchione" <ke...@lysseus.com> wrote:
[about the seemingly OO-insoluble rock-paper-scissors problem,
and the lack of "multimethods" in java, c++, tads, inform, etc]

> Bringing this back down to an IF language, what would be the
> best way to model this behavior in TADS 3?

The way I'd personally approach this particular problem in tads 3 would be
to not worry so much about OO purity, and instead observe that
rock-paper-scissors is essentially a trivial exercise in modular arithmetic:

class RPSThing: object
beats(other) { return rpsVal == (other.rpsVal + 1) % 3; }
;
class Paper: RPSThing rpsVal = 0;
class Scissors: RPSThing rpsVal = 1;
class Rock: RPSThing rpsVal = 2;

That's kind of dodging the question, though, since the rock-paper-scissors
thing was merely a simplified example; the original issue in this thread
wasn't how to implement rock-paper-scissors, but rather... um... what was it
again? Let's see... okay, the original issue in the *subthread* was that
certain problems don't model well with single-dispatch polymorphism but work
better with something like dylan/common lisp "multimethods."

So, how would you do something similar in tads or java or the like? I think
you'd have to do some explicit second-level dispatching. One way to do this
is to create intermediate objects a la the "visitor pattern," but that's a
pretty heavyweight approach to this particular problem. You can do it more
easily by doing something that's syntactically very similar to overloading.
The trick is that rather than attempting to use overloading, you stick to
what OO languages are *actually* good at, which is polymorphism. The base
r-p-s object provides a public interface with a single "beats(other)"
method. Each subclass overrides this to compare itself to the other object.
The naive way of doing this is by sensing the type of the other object in
each override:

class RPSThing: object
beats(other) { return nil; } // abstract; to be overridden everywhere
;
class Paper: RPSThing
beats(other) { return other.ofKind(Rock); }
;

...and so on with the other types. It works, but it's not very OO. Now,
this is where we bring in the bit I mentioned about something that looks
like overloading but isn't. Rather than actual overloading, we could define
separate methods for "am I beat by paper?", "am I beat by rock?", and "am I
beat by scissors?". We still have the single public interface method,
beats(other), which everyone overrides. But now, the overrides simply call
the appropriate "am I beat by..." on the other object.

class RPSThing: object
beats(other) { return nil; } // abstract; to be overridden everywhere
beatByPaper() { return nil; }
beatByRock() { return nil; }
beatByScissors() { return nil; }
;
class Paper: RPSThing
beats(other) { return other.beatByPaper(); }
beatByScissors() { return true; }
;

and you can guess how the other subclasses look. There's no overloading
going on, just nice simple polymorphism. This kind of coding is sometimes
called "double dispatch," because we're doing two separate polymorphic
override selections. When we call a.beats(b), we first figure out which
subclassed version of beats() to call - this is the first dispatch. But
then, that version of beats() will call another method that is itself
polymorphic - beatByPaper, beatByScissors, or beatByRock. This is the
second dispatch. Note that this approach isn't very different from the
original (non-working) overloading attempt from far upthread; the main
difference is that we have a couple of extra lines of code for the base
class beatByXxx() definitions.

Finally, note that you *could* now use overloading in the beatByXxx methods
if you translated this to C++ or java. It doesn't change the logic, since
(as I've been trying to explain in this thread) overloading is just a
compile-time syntax feature with no dynamic capabilities, but some people
might prefer the more concise overloaded syntax. Translating the above to
java:

class RPSThing
{
public abstract boolean beats(RPSThing other);
protected boolean beatBy(Rock other) { return false; }
protected boolean beatBy(Paper other) { return false; }
protected boolean beatBy(Scissors other) { return false; }
}
class Paper extends RPSThing
{
public boolean beats(RPSThing other) { return other.beatBy(this); }
protected boolean beatBy(Scissors other) { return true; }
}

I'll leave it as an exercise to the reader to explain why overloading will
work in this new design but didn't work in the upthread proposals that I
objected to. (Hint: it's the 'this'.)

David A. Cornelson

unread,
Apr 12, 2003, 12:23:49 AM4/12/03
to
"Mike Roberts" <mjrUND...@hotmail.com> wrote in message
news:ELGla.20$037...@news.oracle.com...

> "Kevin Forchione" <ke...@lysseus.com> wrote:
> [about the seemingly OO-insoluble rock-paper-scissors problem,
> and the lack of "multimethods" in java, c++, tads, inform, etc]
> > Bringing this back down to an IF language, what would be the
> > best way to model this behavior in TADS 3?
>

<snip cool code>

>
> I'll leave it as an exercise to the reader to explain why overloading will
> work in this new design but didn't work in the upthread proposals that I
> objected to. (Hint: it's the 'this'.)
>

So "theoretically" an IF language could implement a modifier on methods or
arguments that would "underneath the covers" do double-dispatch and give the
programmer this run-time overload ability. I guess I wouldn't do it on each
argument since that scares me, but on the method might not be bad...I'd also
force the compiler to tell the programmer that they needed a "default"
overload in case none of the available ones matched....

Sort of like:

class serviceRPS
{
dynamic beats(Rock obj1, Scissors obj2) {...}
dynamic beats(Rock obj1, Paper obj2) {...}
....
dynamic default beats(Object obj1, Object obj2) {...}
}

Then when this code is compiled, the double-dispatch or some other method
(even maybe dynamic assembly calling) could be written to the
executable/game file/whatever.

Right?

Dave


Edmund Kirwan

unread,
Apr 12, 2003, 7:31:52 AM4/12/03
to
"David A. Cornelson" <david dot cornelson at iflibrary dot com> wrote in message news:<ihCdnUcTVOt...@speakeasy.net>...

> "Mike Roberts" <mjrUND...@hotmail.com> wrote in message
> news:ELGla.20$037...@news.oracle.com...
> <snip cool code>

Doh!!!

All hail mjr underscore ...

That'll learn me to code on the fly.

I'll be in the corner if anyone needs me. Chap in the tall hat ...

ed.

PS He mentioned that the visitor approach was a bit heavy-weight for
this. As a healthy mix of completeness, humble-pie-gorging, and
homework-punishment, here's proving his point ...

class RPSThing {
boolean beats(Scissors scissors) {
return false;
}
boolean beats(Paper paper) {
return false;
}
boolean beats(Rock rock) {
return false;
}
boolean beats(RPSThing rpsThing) {
return false;
}
void accept(Visitor visitor) {
visitor.visit(this);
}
}
----
class Rock extends RPSThing {
boolean beats(Scissors scissors) {
return true;
}
void accept(Visitor visitor) {
visitor.visit(this);
}
}
----
class Scissors extends RPSThing {
boolean beats(Paper paper) {
return true;
}
void accept(Visitor visitor) {
visitor.visit(this);
}
}
----
class Paper extends RPSThing {
boolean beats(Rock rock) {
return true;
}
void accept(Visitor visitor) {
visitor.visit(this);
}
}
----
class Visitor {
public RPSThing thing1 = null;
public boolean thing1Wins = false;
void visit(Rock rock) {
thing1Wins = thing1.beats(rock);
}
void visit(Paper paper) {
thing1Wins = thing1.beats(paper);
}
void visit(Scissors scissors) {
thing1Wins = thing1.beats(scissors);
}
void visit(RPSThing rpsThing) {
thing1Wins = false;
}
}
----
class Game {
public static void main(String[] args) {
RPSThing thing1 = getThing();
RPSThing thing2 = getThing();
Visitor visitor = new Visitor();
visitor.thing1 = thing1;
thing2.accept(visitor);
if (visitor.thing1Wins) {
System.out.println(thing1 + " beats " + thing2);
} else {
System.out.println(thing2 + " beats " + thing1);
}
}

static RPSThing getThing() {
int a = (int)(Math.random() * 3);
switch (a) {
case 0:
return new Rock();
case 1:
return new Paper();
default:
return new Scissors();

Mike Roberts

unread,
Apr 12, 2003, 6:03:48 PM4/12/03
to
"David A. Cornelson" <david dot cornelson at iflibrary dot com> wrote:
[about the "double dispatch" approach to rock-paper-scissors]

> So "theoretically" an IF language could implement a modifier on
> methods or arguments that would "underneath the covers" do double-
> dispatch and give the programmer this run-time overload ability.

Absolutely. In fact, my guess would be that the languages with
"multimethods" are doing something along the same lines, but I don't know
how they implement it. There are other approaches they could use, like the
reflection stuff we talked about earlier, but double (or n-ary) dispatch
seems a lot more efficient - at run-time, it's just a couple of pointer
dereferences per polymorphic argument, so the run-time overhead is minimal.

Horpner

unread,
Apr 14, 2003, 11:44:56 AM4/14/03
to
"Edmund Kirwan" <ade...@eircom.net> wrote in message
news:a80e1059.03041...@posting.google.com...

> "David A. Cornelson" <david dot cornelson at iflibrary dot com> wrote in
message news:<ihCdnUcTVOt...@speakeasy.net>...
> > "Mike Roberts" <mjrUND...@hotmail.com> wrote in message
> > news:ELGla.20$037...@news.oracle.com...
> > <snip cool code>
>
> Doh!!!
>
> All hail mjr underscore ...
>
> That'll learn me to code on the fly.
>
> I'll be in the corner if anyone needs me. Chap in the tall hat ...
>
> ed.
>
>
>
> PS He mentioned that the visitor approach was a bit heavy-weight for
> this. As a healthy mix of completeness, humble-pie-gorging, and
> homework-punishment, here's proving his point ...

SNIP neat example code in TADS.

In the visitor pattern I'm thinking of, though, the RPSThing instances
would not know about each other. The Visitor instances would be
the ones that know which objects beat which.

I suppose that's why it's not a great application of the Visitor
Pattern, whose major drawback is that every visitor needs to know
about every type of object in the hierarchy that they visit. The advantage
is that those objects don't need to know about each other, which is
counterintuitive for the RPS problem.

Neil C.


Edmund Kirwan

unread,
Apr 15, 2003, 6:17:16 PM4/15/03
to
"Mike Roberts" <mj...@hotmail.com> wrote in message news:<830ma.924$3%5.110...@newssvr15.news.prodigy.com>...

> --Mike
> mjr underscore at hotmail dot com

Now I'm having a crisis. I wanted to post this to a more dedicated OO
usenet but felt too dirty and ashamed.

Let's say I have three classes. A superclass: Organism, a subclass of
Organism called Human, and another subclass of Organism called Dog.

I want to be really polymorphic, so I put a walk() method in Organism,
which both Human and Dog implement with their own pecularities of
walk().

Then I want to add readShakespeare(), which I feel should go in Human
alone(dogs prefer magazines).

But this means that in the code I will have to have something that
will distinguish the compile-time class (such as a visitor); whereas
I'd like as much as possible to just pass around Organisms, and let
polymorphism worry about which subclass is being dealt with.

But I've gone off visitors. All this code-distinguishing has left a
sour taste.

I could, of course, implement a default readShakespeare() in Organism,
which could just return a boolean to say that the Object that this
this method was called on doesn't support it.

This way, I never have to distinguish compile-time class, I can call
this method on any subclass of Organism, and take appropriate action
based on the boolean return.

But this seems ... unaesthetic.

Only Humans read Shakespeare. Why should Organism have to know about
it at all, even if it is just to say, "I don't do that."

I suppose my questions boil down to this:
1) Using pure polymophism, are we forced to have a superclass that has
ALL methods of subclasses, even those methods that only makes sense to
certain subclasses?
2) Should I grow up and stop worrying about, "Aesthetics," when the
cold, harsh, professional climate of deadline-meeting really just
says, "If polymorphism gives you a simple, short answer: use it."

.ed

Mike Roberts

unread,
Apr 15, 2003, 7:15:42 PM4/15/03
to
"Edmund Kirwan" <ade...@eircom.net> wrote:
> Let's say I have three classes. A superclass: Organism, a
> subclass of Organism called Human, and another subclass
> of Organism called Dog.
>
> Then I want to add readShakespeare(), which I feel should
> go in Human alone(dogs prefer magazines).
>
> I could, of course, implement a default readShakespeare() in
> Organism, which could just return a boolean to say that the
> Object that this this method was called on doesn't support it.
>
> But this seems ... unaesthetic.
>
> Only Humans read Shakespeare. Why should Organism
> have to know about it at all, even if it is just to say, "I don't
> do that."
>
> I suppose my questions boil down to this:
> 1) Using pure polymophism, are we forced to have a
> superclass that has ALL methods of subclasses, even those
> methods that only makes sense to certain subclasses?

I think the real question you have to ask yourself is why you're trying to
call readShakespeare() on an object when all you know about the object is
that it's an Organism. If you can answer that question, a reasonable class
design will probably emerge.

If you want to be able to call a method on any object of a given base class,
then, yes, you have to put that method in the base class. But excessively
fat base classes are warning signs of bad OO design, because fat base
classes frequently have the same problems that are making you uneasy about
your example.

My experience has been that when you're adding a method to a base class, and
that method seems terribly out of place in the base class because it
represents some specialized facet of a subclass, then you should re-examine
your motivation for putting it in the base class in the first place. In
many cases, maybe even most cases, the motivation is coming from some code
that is incorrectly operating on the base class. In other words, whatever
code is calling readShakespeare() on an Organism probably shouldn't be
dealing with an Organism in the first place; it should probably be dealing
with the Human subclass instead, because in the act of calling
readShakespeare(), it's assuming that it's dealing with an object that reads
Shakespeare. In other cases, you really do want to be dealing with the base
class, but the semantics of the method haven't been properly abstracted, so
they seem to apply specially to the subclass only because you haven't found
the more generalized meaning that applies to all members of the base class.

I don't know exactly what you have in mind with your example, but my
intuition is that readShakespeare() is an example of the latter problem.
Specifically, if you answer my first question and decide you really do want
to put this method on Organism, then it would make more sense if the method
were something like interactWithBook(Book) instead. Organism being
abstract, it wouldn't implement the method. Amoeba.interactWithBook() would
probably just add a layer of slime to the book. Dog.interactWithBook()
might chew the book up or bury it in the back yard, depending on the
personality of the dog. For certain Human subclasses, interactWithBook()
would read the book and appreciate it to varying degrees; for other Human
subclasses, the method might chew up the book or bury it in the back yard.

Greg Ewing (using news.cis.dfn.de)

unread,
Apr 15, 2003, 10:35:09 PM4/15/03
to
Edmund Kirwan wrote:
> I suppose my questions boil down to this:
> 1) Using pure polymophism, are we forced to have a superclass that has
> ALL methods of subclasses, even those methods that only makes sense to
> certain subclasses?

This depends on the language and implementation. If
your compiler insists on compile-time static typing,
then yes, because that's the only way you can do what
you want to do within the type system.

But more dynamic OO languages give you other possibilities.
In Python or Smalltalk (or Tads, since we're talking about
IF) there are ways of asking "Does this object support the
readShakespeare operation?" Based on the result of that,
you can then decide whether to attempt calling it.
This is a perfectly legitimate thing to do.

There's also a middle ground which is supported even
by most statically-typed OO languages (e.g. C++), and
that's to ask at run time whether an object belongs to
a particular class or one of its subclasses. For instance,
you would ask whether the object belonged to the class
Human, and if so, you could infer that it will have a
readShakespeare method.

But that's not as flexible as testing for the presence
of individual features, because it means that *only*
Humans or something derived from a Human will be able
to read Shakespeare as far as that particular
piece of code is concerned. If later you decide to add
an Android class that also needs to be able to read
Shakespeare, you are faced with either making it a
subclass of Human, which wouldn't really be right since
it would probably inherit other things that Androids
shouldn't have, or tracking down all the places where
you've assumed that only Humans can do certain things
and revising them.

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Daniel Barkalow

unread,
Apr 16, 2003, 10:04:20 PM4/16/03
to
On Wed, 16 Apr 2003, Greg Ewing (using news.cis.dfn.de) wrote:

> Edmund Kirwan wrote:
> > I suppose my questions boil down to this:
> > 1) Using pure polymophism, are we forced to have a superclass that has
> > ALL methods of subclasses, even those methods that only makes sense to
> > certain subclasses?
>
> This depends on the language and implementation. If
> your compiler insists on compile-time static typing,
> then yes, because that's the only way you can do what
> you want to do within the type system.
>
> But more dynamic OO languages give you other possibilities.
> In Python or Smalltalk (or Tads, since we're talking about
> IF) there are ways of asking "Does this object support the
> readShakespeare operation?" Based on the result of that,
> you can then decide whether to attempt calling it.
> This is a perfectly legitimate thing to do.
>
> There's also a middle ground which is supported even
> by most statically-typed OO languages (e.g. C++), and
> that's to ask at run time whether an object belongs to
> a particular class or one of its subclasses. For instance,
> you would ask whether the object belonged to the class
> Human, and if so, you could infer that it will have a
> readShakespeare method.

Java has a 3/4-ground: you can declare interfaces, which are sets of
method signatures that a class can implement. Then you do:

if (actor instanceof Erudite)
((Erudite) actor).readShakespeare();
else
actor.doNothing();

The interface doesn't have to mean anything more than that the class has
that method, although it could refer to a group of related methods. This
is somewhat nice, because it means there's a single place which declares
what the readShakespeare() method is all about. Of course, java has lousy
syntax and requires the typecast even though you've just tested to see
whether the object is actually of the desired type.

Of course, this runs into problems when you initially think there isn't a
need for an interface for something, and just put it on a class (or a
larger interface); but the individual method approach runs into problems
when you define methods with the same name on unrelated objects, and end
up calling methods you didn't mean to use in the same context (sure, you
can just not call the methods the same thing, but that makes method names
a single global namespace).

Kevin Forchione

unread,
Apr 16, 2003, 10:14:15 PM4/16/03
to
"Daniel Barkalow" <iabe...@iabervon.org> wrote in message
> Java has a 3/4-ground: you can declare interfaces, which are sets of
> method signatures that a class can implement. Then you do:
>
> if (actor instanceof Erudite)
> ((Erudite) actor).readShakespeare();
> else
> actor.doNothing();

TADS 3 can implement interfaces as well, only at runtime, instead of compile
time. For instance, proteus library extension would do the same kind of
thing with:

if (actor.implementsIF(Erudite)
actor.readShakespeare();
else
actor.doNothing();

--Kevin


Greg Ewing (using news.cis.dfn.de)

unread,
Apr 16, 2003, 11:17:19 PM4/16/03
to
Daniel Barkalow wrote:
> (sure, you
> can just not call the methods the same thing, but that makes method names
> a single global namespace).

The same thing happens in effect if you give your root
class stub methods for everything.

Anyway, in a dynamically typed language, it's probably
not a good idea to give completely unrelated methods the
same name in two classes that might get accidentally
substituted for each other.

Benjamin Fan

unread,
Apr 18, 2003, 11:58:53 PM4/18/03
to
From the perspective of Free software, there are fewer advantages
of encapsulation. If the main purpose of encapsulation is to
protect objects from meddling programmers, then this advantage is
removed entirely since programmers can simply modify the object
source code to meet their needs.

Regarding the Java version of Rock-Paper-Scissors, I
implemented it in the below fashion. Java has the advantage of
reflection where objects can report the name of their class.
This also gives it the ability to "plug in" new types of weapons
(like the grenade (a rock, with your thumb sticking out)) without
having to re-edit the existing Rock/Paper/Scissors code. (Some
source code lines have been omitted):

interface Rochambeau {
public int beats(Object you);
public int isBetterThan(Object you);
}

public class Weapon implements Rochambeau {
public int beats(Object you) {
if (you == null)
return UNKNOWN;
if ( ! (you instanceof Rochambeau))
return UNKNOWN;
int my_belief = this.isBetterThan(you);
int your_belief = ((Rochambeau) you).isBetterThan(this);
if (my_belief == UNKNOWN)
return reverse(your_belief);
else
return my_belief;
}
}

public class Rock extends Weapon {
public int isBetterThan(Object you) {
String your_name = you.getClass().getName();
String my_name = this.getClass().getName();
if (your_name.equals("Paper"))
return LOSE;
if (your_name.equals("Scissors"))
return WIN;
if (your_name.equals(my_name))
return TIE;
return UNKNOWN;
}
}
public class Grenade extends Weapon {
public int isBetterThan(Object you) {
String your_name = you.getClass().getName();
String my_name = this.getClass().getName();
if (your_name.equals("Rock"))
return WIN;
if (your_name.equals("Paper"))
return WIN;
if (your_name.equals("Scissors"))
return WIN;
if (your_name.equals(my_name))
return TIE;
return UNKNOWN;
}
}

Does reflection really have high overhead costs?

Ben

--
To get my current email address, concatenate these three strings:
1. "benjamin_fan" 2. "_2002a" 3. "@yahoo.com"
It will look a lot like: xxxxxxxx_yyy_zzzzz @ yahoo . com

Joe Mason

unread,
Apr 19, 2003, 2:31:14 PM4/19/03
to
In article <9ea00d5e.03041...@posting.google.com>, Benjamin Fan wrote:
> From the perspective of Free software, there are fewer advantages
> of encapsulation. If the main purpose of encapsulation is to
> protect objects from meddling programmers, then this advantage is
> removed entirely since programmers can simply modify the object
> source code to meet their needs.

This is, to be sure, one of the advantages of open source. However, it
still makes it more difficult, because now you can't just distribute
your new code and expect it to work for everyone else, so you have to
distribute the code you changed as well (and that will seriously annoy
people who have to have two versions of a library just for your
software). Also, if it's actually being done for security, the security
framework should be testing the checksums of the code before running it
to be sure it hasn't been altered.

<snip>

> public class Rock extends Weapon {
> public int isBetterThan(Object you) {
> String your_name = you.getClass().getName();
> String my_name = this.getClass().getName();
> if (your_name.equals("Paper"))
> return LOSE;
> if (your_name.equals("Scissors"))
> return WIN;
> if (your_name.equals(my_name))
> return TIE;
> return UNKNOWN;
> }
> }
>
> public class Grenade extends Weapon {
> public int isBetterThan(Object you) {
> String your_name = you.getClass().getName();
> String my_name = this.getClass().getName();
> if (your_name.equals("Rock"))
> return WIN;
> if (your_name.equals("Paper"))
> return WIN;
> if (your_name.equals("Scissors"))
> return WIN;
> if (your_name.equals(my_name))
> return TIE;
> return UNKNOWN;
> }
> }

But this is just the big switch statement that we're trying to avoid!
Notice that Rock still knows nothing about Grenade, and if you add more
stuff that you want Grenade to be better than, you have to add to
Grenade's code directly. That's why it's not "encapsulated".

BTW, you don't need reflection to do this. You can just add a
"weapon_name" field to Weapon. In fact, that's just how I would do it
if I were writing a game like this, because the extra confusion of
double dispatch and other weird things isn't worth it in this case.

But if I were writing a library or framework (which is what Dave and
Mike are doing) I'd think hard about whether it's worth adding more
complex dispatch methods in a way that makes it easy for the end user.
Most of the problem with complexity comes when everybody tries to roll
their own. Pushing it down into the library *might* be acceptable.

Joe

David A. Cornelson

unread,
Apr 19, 2003, 4:48:25 PM4/19/03
to
"Joe Mason" <j...@notcharles.ca> wrote in message
news:slrnba35f...@gate.notcharles.ca...

> In article <9ea00d5e.03041...@posting.google.com>, Benjamin Fan
wrote:
>
> But if I were writing a library or framework (which is what Dave and
> Mike are doing) I'd think hard about whether it's worth adding more
> complex dispatch methods in a way that makes it easy for the end user.
> Most of the problem with complexity comes when everybody tries to roll
> their own. Pushing it down into the library *might* be acceptable.

I'd say Mike is a tad ahead of me.

Dave


Mike Roberts

unread,
Apr 19, 2003, 6:52:22 PM4/19/03
to
"Benjamin Fan" <junkaccou...@yahoo.com> wrote:
> From the perspective of Free software, there are fewer advantages
> of encapsulation. If the main purpose of encapsulation is to protect
> objects from meddling programmers, then this advantage is
> removed entirely since programmers can simply modify the object
> source code to meet their needs.

You're operating under a bit of a misconception, I think. Implementation
hiding in OO design isn't a security measure to protect your library classes
against malicious programmers who want to reverse-engineer their way into
your implementation; that's more in the realm of distributing .o files
instead of .c files.

Implementation hiding is a form of compiler-assisted quality assurance; it
the same sort of thing as declaring types for your variables. It lets the
programmer who's working on a class implementation declare that certain code
is the public interface and certain code is the internal implementation, in
such a way that clients of that class can plainly see what the class
designer intended, and in such a way that the compiler can assist by
checking everyone's work to ensure that everyone's on the same page.

It's an especially valuable discipline for projects involving multiple
programmers, and *especially* for projects where the programmers aren't in
constant close contact. In other words, I'd claim the exact opposite of
your assertion - implementation hiding is *more* valuable for projects using
"free software" development methodologies than for less distributed models.

Adam Thornton

unread,
Apr 19, 2003, 7:40:07 PM4/19/03
to
In article <0sSdnZh2SqT...@speakeasy.net>,

David A. Cornelson <david dot cornelson at iflibrary dot com> wrote:
>I'd say Mike is a tad ahead of me.

I think he's 3 TADS ahead of you! Haw! Haw!

Er, sorry.

Adam

Neil Cerutti

unread,
Apr 21, 2003, 11:05:22 AM4/21/03
to
In article <b7ies4$19bup$1...@ID-169208.news.dfncis.de>, Greg Ewing (using news.cis.dfn.de) wrote:
> Edmund Kirwan wrote:
>> I suppose my questions boil down to this:
>> 1) Using pure polymophism, are we forced to have a superclass that has
>> ALL methods of subclasses, even those methods that only makes sense to
>> certain subclasses?
>
> This depends on the language and implementation. If
> your compiler insists on compile-time static typing,
> then yes, because that's the only way you can do what
> you want to do within the type system.
>
> But more dynamic OO languages give you other possibilities.
> In Python or Smalltalk (or Tads, since we're talking about
> IF) there are ways of asking "Does this object support the
> readShakespeare operation?" Based on the result of that,
> you can then decide whether to attempt calling it.
> This is a perfectly legitimate thing to do.
>
> There's also a middle ground which is supported even by most
> statically-typed OO languages (e.g. C++), and that's to ask at
> run time whether an object belongs to a particular class or one
> of its subclasses. For instance, you would ask whether the
> object belonged to the class Human, and if so, you could infer
> that it will have a readShakespeare method.
>
> But that's not as flexible as testing for the presence of
> individual features...

Not if you create an interface class:

class Reader: public Organism;

class Human: public Reader;

class Anrdoid: public Reader;

class Lichen: public Organism;

Then the extensibility problem you note later will not occur. If
you are operating on a container of Organisms, you can query
objects for the Reader interface.

--
Neil Cerutti

0 new messages