The Subtle Joys of Generic Methods
common-lisp (2), language-design (4), python (59)你想的时候红宝石, what is your first association? Oh, it's Rails? Hmm. What is you think红宝石feature?Monkey patching?Yep, me too. Programmers have stronger and more heated opinions about monkey patching than adolescent girls have about glitter. Globally modifying class functionality at runtime? Why yes, that does sound dangerous.
With a little imagination you can imagine a doomsday scenario where two libraries wrestle with each other continually to replace some piece of functionality with their own preferred version, and with each release the library maintainers modify their code to move their library back to the top of heap1.
但是,猴子修补程序确实提供了一个重要问题:在运行时动态地将功能添加到类。在Python土地上,我们对这个问题没有很好的解决方案,我们有点决定我们对此:最好不要有动物园,而不是让狮子偶尔逃脱和啃着小孩子。
However, there is a great solution to this problem. One that allows dynamically adding functionality to a class--nay, to classes--while respecting namespaces and not mucking up the reasonable assumptions in other peoples' code (primarily the assumption that a function won't, without warning, suddenly begin behaving differently, which is a rather important assumption for writing even moderately deterministic code):
就像许多慢慢看到现代编程语言的伟大想法一样,这个想法来自Lisp - 更具体地说,普通的Lisp - 但特别是这一人尚未以主流语言重叠。解决方案?泛型功能(也称为多方法),如那些CLOS. Let's do a few examples of what Python might be like if it had generic function based OO.
班级Person(object):name=None标题=Nonedef迎接(Personp):打印“你好%S.%S."%(p.name,p.标题)
So the key difference is that some parameters are typed with a class. Lets say that anything that isn't explicitly typed can be of any type, so we're using an eclectic mix of strong typing and duck-typing (similar to Objective-C).
The first advantage we get is that we can add functionality to a class from any module, not just in its class declaration. For example, if the above code is in a module namedPersonModule
, then we could write this code in another module:
fromPersonModuleimportPerson,迎接deffarewell(Personp):打印“再见%S.%S."%(p.name,p.标题)
def迎接_goodbye(Personp):打印迎接(p)+“,”farewell(p)
p=Person()p.name='Will'p.标题='Mr.'迎接(p)# print "Hello Mr. Will"farewell(p)# print "Goodbye Mr. Will"
So we can add methods to an object declared in a different module, but what if we want to override an object's default behavior in our module? Easy as pie.
fromPersonModuleimportPerson,迎接_goodbyedeffarewell(Personp):return"k thnx bai%S."%p.name
p=Person()迎接_goodbye(p)#打印“你好先生将k非常感谢白将“
So that's kind of cool, right? Sure, I'm pulling this out of thin air and can't even prove to you that what I'm suggesting is possible, but thisishow OO works with CLOS. There is a working precedent.
而且很棒。
通用函数的快乐也比这更进一步,给我们一些功能,感觉类似于Erlang或Scala中的类型匹配。考虑此代码:
班级Person:name=None班级Dog:name=None
defspeak(a):'If none of the more specific patterns match, falls back here.'打印"This is a%S."%a
defspeak(Personp):打印"My name is%S."%p
defspeak(Dogd):打印"Woof"
def走(Personp,Dogd):打印"%S.takes%S.for a walk"%(p,d)
def走(Dogd,Personp):打印"%S.不能走%S."%(d,p)
def走(Personp,Personp):打印"Um. That's weird."
a=Person()a.name="Will"b=Dog()b.name="Leo"c=Person()c.name="Jim"
走(a,b)# "Will takes Leo for a walk"走(b,a)# "Leo cannot walk Will"走(a,c)#“嗯。那很奇怪。”
For those who have used 'real' pattern matching, this will seem like a very cumbersome syntax, but fortunately generic methods don't只是give us pattern matching, along with the heavier syntax comes heavier capabilities:
- Rather than only matching on types, the ability to use classes for pattern matching as well. (You could also phrase this as, classes are a valid type for matching.) That includes giving us polymorphism by specializing methods on increasingly specific classes (
Programmer
instead ofPerson
,PerlProgrammer
instead ofProgrammer
). - Ability to override handling of a specific pattern, without rewriting the rest of the patterns.
也就是说,它不一定允许模式匹配的所有功能,因为您无法自定义它将尝试匹配模式的顺序(将在可预测和一致中始终从更具体到的更常规。订购,而模式匹配将让您定义任何您的心愿欲望)2.
Also, depending on the implementation it might not allow specializing on values (instead of just types), but it would be fairly intuitive to add value-specialization to the syntax:
defadd(a,b):returna+bdefadd(a,0):returna
That would open up some pretty interesting doors. Doors I would like to walk through. (Actually, looks like those doors are alreadydiscussed here, andopened here. Now I just need to start walking. Err, and I'd like the syntax to be native. Maybe a pre-compiler of some sort is in order.)
This sounds stupid, doesn't it? It is, and the tragedy is that something comparable occurs all the time in the worlds of anti-virus and toolbars.↩
Depending on the specific implementation (basically, where one establishes the trade-off between explicitly importing all multi-methods you'll use versus them being implicitly imported when you load a module to save typing), it is possible to create a predictable system for multiple inheritance, although at the expense of many many more imports.↩