[Edu-sig] Quadrays again... cross-training in two langauges

Kirby Urner kurner at oreillyschool.com
Mon Aug 10 21:58:55 CEST 2015


On Mon, Aug 10, 2015 at 12:02 PM, Mark Engelberg <mark.engelberg at 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/edu-sig/attachments/20150810/4017602e/attachment-0001.html>


More information about the Edu-sig mailing list