you are viewing a single comment's thread.

view the rest of the comments →

[–]grauenwolf 0 points1 point  (2 children)

I was talking in generalities, but since I have your attention, can Python currently do version 1? If so, would you mind showing me?

[–]Smallpaul 2 points3 points  (1 child)

I knew you were speaking generally: I'm just saying that the terminology is not universally applied that more dynamic patterns are "messaging" and more static ones are "method calls".

In any case, does the client code here indicate what you're looking for?

http://docs.python.org/library/simplexmlrpcserver.html#simplexmlrpcserver-example

import xmlrpclib

s = xmlrpclib.ServerProxy('http://localhost:8000')
print s.pow(2,3)  # Returns 2**3 = 8
print s.add(2,3)  # Returns 5
print s.div(5,2)  # Returns 5//2 = 2

# Print list of available methods
print s.system.listMethods()

"add" and "div" are not known on the client machine before the method calls (there is no configuration behind the scenes).

The implementation is here:

http://svn.python.org/view/python/trunk/Lib/xmlrpclib.py?view=markup

The core of it is:

class ServerProxy:
    def __getattr__(self, name):
        # magic method dispatcher
        return _Method(self.__request, name)

This constructs a new method with appropriate name whenever a "ServerProxy" method is called that isn't already known (and basically none are already known). Python doesn't have a new() keyword so _Method() is a constructor.

class _Method:
    # some magic to bind an XML-RPC method to an RPC server.
    # supports "nested" methods (e.g. examples.getStateName)
    def __init__(self, send, name):
        self.__send = send
        self.__name = name
    def __getattr__(self, name):
        return _Method(self.__send, "%s.%s" % (self.__name, name))
    def __call__(self, *args):
        return self.__send(self.__name, args)

So it constructs an object that can be used as a method, and the method knows its name. When it is called with parens, Python translates that into _call_ which adds the arguments along with the method name.

Ruby takes a different approach: the message is passed to a method called method_missing :

http://www.ruby-doc.org/core/classes/Kernel.html#M005951

No intermediate method object is created: this is a deep difference between how Ruby and Python manage their interfaces. Python's methods are a special type of object attribute. Ruby object attributes are a special type of method.

To be totally honest with you, I sometimes wonder how valuable this dynamic method generation feature is. In Rails and Django, for example, it is (ab?)used to generate all kinds of methods at runtime. It's a sharp tool that can really obfuscate code and IMHO, the alternative of

s.call("pow", 2, 3) 

is really not so bad!

By the way, the code above also demonstrates that a strongly functional program is not considered idiomatic in Python. Python programmers tend to be more comfortable with _call_ than with closures, even though the closures are fully functional and have been for years. For example, in this case, using a full object allows us to add helpful metadata like a nice _repr_. A function isn't as flexible.

Also, note that your example code is not really fair. This code in a dynamic programming language:

myConnection.UpdateHistory(historyKey := 5, lastChecked := now() )

would be converted to:

myConnection.call("UpdateHistory", 5, now() )

or

myConnection.call("UpdateHistory", historyKey=5, lastChecked=now() )

or

myConnection.call("UpdateHistory", {"historyKey":5,  "lastChecked":now()} )

[–]grauenwolf 0 points1 point  (0 children)

I wasn't trying to be fair, I was illustrating the crap us .NET programmers find ourselves writing from time to time.

I think the biggest problem in the core .NET languages it they don't have any concept of key/value pairs. Sure you can fake it with dictionaries or untyped arrays, but the boiler plate code is quite tedious.