[OPINION] - does language really matter if they all do the samething?

Dietrich Epp dietrich at zdome.net
Sat Jan 24 00:44:15 EST 2004


Please understand, before you read this, that I think Python is the 
most elegant language that looks and feels natural.  My point is that 
there are plenty of programming techniques which aren't natural in 
Python, but are still useful.  I feel Python's strength is that it is 
flexible enough that most patterns that many programmers use day-to-day 
map very easily and naturally into readable Python code.  The problem 
is that often when you use a pattern that doesn't fit the language you 
use, it will be difficult to code, harder to maintain, and behave 
unexpectedly meanwhile.

The rest of the post is dedicated to two examples of patterns which 
don't quite fit into Python, but are used well in some projects written 
in other languages.

On Jan 23, 2004, at 5:04 PM, Paul Prescod wrote:

> Dietrich Epp wrote:
>> ...
>> <abestos>
>> Python seems to take the middle ground.  It's build from a procedural 
>> standpoint, in an environment where most things are objects and some 
>> are functions.  It's not what I'd choose as an example of an object 
>> oriented language.  There are no messages, only function calls, so 
>> you can't capture them.
>
> That's certainly not true. Take a look at how the XML-RPC libraries 
> work. They capture "messages" sent from the program to the proxy 
> object, reify them as XML "messages" sent across the network and then 
> turned back into function calls applied to objects.

Well, I don't mean that you absolutely can't capture a message, but 
it's just not something that's natural in Python the way it is in 
Objective C.  In Objective C, I can create an object which doesn't 
respond to some message "foo".  However, if I send "foo" to the object, 
it gets a chance to respond.  In Python, there is no concept of 
messages.  You would have to override the function that gets an 
object's attribute and create a function for it to return.  You can 
capture function calls, but capturing method calls is a two-step 
process in Python which requires a little bit of magic to implement.

The problem is that I didn't define message.  In python, sending the 
"bar" message to the "foo" object is written as "foo.bar()".  But it 
isn't the same as sending a message, because in Python, it's two steps: 
get the "bar" property of "foo", and call it.

The example of this is at the end of this post.

>> The plus side is that methods behave exactly the same as
>> functions, this makes the language simple and the two ideas 
>> interchangeable.  You can set an object's method to a function.  The 
>> minus side is that the paradigm of sending a message "to" an object 
>> doesn't exist, and anyone coming from Smalltalk or even Objective-C 
>> might miss that and feel constrained (I did).  But if Python were 
>> really object-oriented like that, then it wouldn't have Python's 
>> simplicity any more.
>> </abestos>
>
> I would appreciate an example of something you would do by capturing a 
> message that cannot be done in Python.
>
> I rather wonder if you've illustrated the downside of flitting from 
> language to langauge. You may think you're using a language to 
> capacity without actually doing so. I could be wrong but perhaps even 
> the tasks you turn to Lisp or Perl for may have easy equivalents in 
> Python.

If I wanted to make this personal, I would accuse you of choosing your 
patterns to fit the language rather than vice versa.  Or perhaps no 
useful pattern exists which is difficult in Python and easy in a 
different language?  I love Python too, but it's only one of the tools 
in my box.

Of course, you haven't seen my code, nor I yours.  My most recent Perl 
script was about twenty lines of nothing but regular expressions and a 
couple dictionary operations.  That would have been even less readable 
and writable in Python.  My Lisp project related to an RPG and random 
generation of items.  It had lots of code like the following:

(defun random-sword-magic-power (quality)
   (choose-random-assoc
     quality (poor medium good)
     ((5 0 0) (glows-in-the-dark))
     ((3 3 0) (magically-silent))
     ((1 5 1) (elemental-power (select-random '(earth water air fire))))
     ((0 2 4) (magical-keen-edge))
     ((0 0 2) (append (random-sword-magic-power 'medium)
                      (random-sword-magic-power 'medium)))))

The numbers on the left are probabilities relative to the other 
probabilities in the same column.  So, when generating a random 'poor' 
magic quality of a sword, you have a 5/9 chance of getting 'glow in the 
dark'.  For a 'good' quality, you have a 2/7 chance of getting two 
'medium' qualities instead.  It is difficult for me to imagine how one 
would go about making this function more concise, to me it looks 
downright minimal.  The only thing I could see making it smaller would 
be removing the parentheses, but they would have to be replaced by 
something else such as commas or tabs.

I'm not trying to say that all applications are like my application, 
and I'm not trying to say that my application can't be written in 
Python.  I'm just saying that using macros, a paradigm that Python 
doesn't even come close to supporting, makes reading and writing 
functions like the above a lot easier.  You don't even need to know 
that 'choose-random-assoc' is a macro, you just need to know how to use 
it.  Heck, defun is a macro in Clisp.

I challenge anyone to come up with a better way to express the above 
function in Python.  If I think it's better, I'll write "PYTHON RULZ" 
on my forehead and post a photo on the web.

On Jan 23, 2004, at 5:35 PM, Brian Quinlan wrote:

> Dietrich wrote:
>> Python seems to take the middle ground.  It's build from a procedural
>> standpoint, in an environment where most things are objects and some
>> are functions.
>
> I'd be interested to know why you don't think that functions are 
> objects. I
> would argue that they are because they have attributes, methods, and 
> are
> bound to variables the same way as other Python objects.

I agree that functions are objects in Python.  However, not everything 
in Python is an object.  For example, names are not objects.  In the 
above example, "foo.bar()", neither "foo" nor "bar" are objects, but 
they are references to objects.  In some languages, such as C and Lisp, 
references to objects can be directly manipulated (Lisp has symbols, C 
has pointers).

>> It's not what I'd choose as an example of an object
>> oriented language.  There are no messages, only function calls, so you
>> can't capture them.
>
> You certainly can capture them. Could you demonstrate the Objective-C 
> or
> Smalltalk syntax for doing so? Maybe it is a lot easier in those 
> languages.

In Objective C:

- (void)forwardInvocation:(NSInvocation *)invocation
{
   if ([someOtherObject respondsToSelector:[invocation selector]])
     [invocation invokeWithTarget:someOtherObject];
   else
     [self doesNotRecognizeSelector:[invocation selector]];
}

In Python:

def __getattr__(self, name):
     try:
         return super(this_class, self).__getattr__(name)
     except AttributeError:
         try:
             method = super(this_class, 
self).some_other_object.__getattr(name)
             if iscallable(method):
                 return method
         except:
             pass
         raise

In Python you have to be careful because this won't normally capture 
just method calls, but all attribute accesses.  In a typical 
Objective-C application you'll probably see a lot of places where the 
program sends a message to an object only if the object responds to 
that message.  Let's compare.

In Objective C:

- (void)resetToDefaultSettings
{
   if ([delegate respondsToSelector:@selector(defaultSettingsFor:)])
     [self setSettings:[delegate defaultSettingsFor:self]];
   else
     [self setSettings:someFallbackValue];
}

In Python, first try:

def reset_settings(self):
     try:
         self.set_settings(self.delegate.default_settings(self))
     except AttributeError:
         self.set_settings(some_fallback_value)

But wait!  What if there is an error in 
self.delegate.default_settings() which raises an AttributeError?  It 
might confuse the hell out of a programmer who might go looking for the 
bug in reset_settings(), because it's using the fallback value for some 
odd reason.  So we should reimplement it.

Python, second try:

def reset_settings(self):
     method = None
     try:
         method = self.delegate.default_settings
     except AttributeError:
         pass
     if method:
         self.set_settings(method(self))
     else:
         self.set_settings(some_fallback_value)

If you think this example is contrived, maybe you haven't worked with 
the paradigms used in Objective-C very much.  The example above was 
used dozens of times in a class that I wrote in Objective-C which draws 
a grid to the screen, for example, when it asks itself or its delegate 
what color the grid should be, or the maximum scale allowed.  The 
practice is also used heavily by Apple's Cocoa libraries, which are my 
favorite UI libraries of all time to program with.





More information about the Python-list mailing list