On Mon, Aug 10, 2015 at 12:02 PM, Mark Engelberg <mark.engelb...@gmail.com> wrote:
> > >> The idea of an interface has migrated to that of a "protocol" which >> provides a ready namespace for a "record". >> > > A protocol is more than just an "interface by a different name" or an > "interface+namespace". > For those not familiar with Java, it's single inheritance, so we don't have the happy solution of injecting new functionality by adding more base classes. In Java, a class is said to "extend" a base class whereas an interface, a set of promised methods, is something a class "implements". Whereas on my extend only one class at a time, said class may implement many interfaces. I see Clojure taps that ability too: http://clojure.github.io/clojure/clojure.core-api.html#clojure.core/extend > > To understand the significance of a protocol, you need to first understand > the "expression problem". The expression problem asks you to consider a > set of polymorphic functions over multiple datatypes. Without controlling > or being able to recompile the original source code, can you easily extend > these functions to a new datatype, and can you easily create a new > polymorphic function across those datatypes? > I like to relate these concepts back to basic math and the different number types we start with, as concentric subsets: N < Z < Q < R < C. Then a protocol or interface is "the operations" such as - / + * < > and so on. What common control panel of operations works across these sets? Clojure, like Java, is not supportive of operator overloading right? I can't go like: (defprotocol VectorOps (__add__ [this other]) (__sub__ [this other]) (len [this]) (__neg__ [this]) (norm [this])) ... and then use (+ A B) to add two vectors, or (- A) to negate one. I think the basics of Polymorphism may be communicated without introducing the wrinkle of not having original source code. Not wanting to change it might be easier for a math student to understand. Not having the original source code sounds like a social problem, an institutional issue, vs. a truly theoretical issue, and that's confusing to newbies. The + and - operators make sense in a Group, where - is both negation and for adding a negative inverse. Add * and / to the mix and we've got a field. Lots of datatypes participate in group and field relationships, including numbers module N, which we may imagine as a specific type of number. That's how I want to anchor the concept of types in Python: in the primitive number types, then add string and collections (sequences, maps...). Mathematics already has a strong sense of "types" which we want to build on. With members of C, we no longer define > or <. >>> complex(1,0) < complex(2, 0) Traceback (most recent call last): File "<console>", line 1, in <module> TypeError: unorderable types: complex() < complex() > > Typically, OO languages make it easy to add a new datatype (just implement > all the methods you want to implement for the new datatype), but hard to > add a new method to an object whose source code you can't recompile. > Even if you don't touch the original class, you're free to subclass it -- that's a very common way of adding functionality to a type. A lot of the standard library works that way. You're not expected to touch the base class, just subclass and override / extend. > Typically, FP languages make it easy to add a new function (just take > cases on the datatype in the body of the function), but no good way to add > a new case for a new datatype to a function whose source code you can't > recompile. > > Clojure has *two *clean, elegant solutions to the expression problem: > protocols and multimethods. Protocols, for example, even let you implement > your own new, custom "methods" on Java's built-in string datatype, which is > what makes them far more powerful than interfaces. (Look up > extend-protocol and extend-type for more info). > > Most languages don't have a solution to the expression problem. Some claim > they have a solution because you can choose what sort of extensbility you > are likely to need (am I more likely to add new datatypes or new > functions?), and code accordingly using either an FP or OO approach. > Python roughly falls into this category, but this is not a true solution to > the expression problem. A true solution in Python involves writing your > programs using the OO approach, and then google for "Python monkey > patching" to look up tricks to inject new methods into classes you don't > control -- it's possible, but generally frowned upon... not particularly > good style in Python or a design principle we would want to teach students. > Here's an example of injecting methods into an existing class, in the second case using a decorator for that purpose. Usually though, subclassing is the way to extend functionality. Note that there's no storing of methods at the instance level. class Obj: def methodA(self): print("MethodA") def methodB(self): print("MethodB") obj = Obj() print([meth for meth in dir(obj) if "meth" in meth]) def methodC(self): print("MethodC") Obj.methodC = methodC # injecting a method post hoc (not organic!) obj = Obj() print([meth for meth in dir(obj) if "meth" in meth]) obj.methodC() def addmeth(m): """Decorator to inject a method""" def rewrite(Klass): setattr(Klass, m.__name__, m) print("Adding", m) return Klass return rewrite @addmeth(methodC) class Obj: def methodA(self): print("MethodA") def methodB(self): print("MethodB") obj = Obj() print([meth for meth in dir(obj) if "meth" in meth]) obj.methodC() Kirby
_______________________________________________ Edu-sig mailing list Edu-sig@python.org https://mail.python.org/mailman/listinfo/edu-sig