Regarding what I meant, I was just interpreting what Stefan said.  I
see two possibilities.  There are container objects like Mul and
objects that go inside them (subclasses of Expr).  The possibilities
are:

1. Container objects do the canonicalization of the objects that go
inside them (this is the way we do it now, at least for Mul and Add).

2. Objects themselves must define how they are canonicalized (i.e.,
using __mul__ and __rmul__).

I saw Stefan as suggesting a third alternative, which I admittedly am
not entirely clear on myself:

3. There are a third kind of object, canonicalizers, which do the
canonicalization.  Somehow the container objects and/or the objects
that go in them hook into them.

Like I said, I'm still vague on how this would actually work, but it
leads to interesting possibilities, like the ability to subclass
canonicalizers.

Or maybe everything there is already possible with the double dispatch rules.

By the way, a Google search reveals that "canonicalizer" has been used
in a similar context before.  According to at least one source, you
can add -er and -ee to any verb to make your own agent noun, as they
are called, and it will be perfectly correct English, even if your
spell checker disagrees.  So maybe we should call the "objects that go
in the containers" canonicalizees.

Aaron Meurer

On Fri, Mar 9, 2012 at 12:47 PM, krastanov.ste...@gmail.com
<krastanov.ste...@gmail.com> wrote:
> Aaron said:
> So you want to separate the canonicalizers from both the objects and
> the containers?  It seems to me that it would be best to just put them
> in the objects themselves, probably just using the Python double
> dispatch system, but this is an interesting idea too.  Would that make
> it more or less extensible?
>
> I would prefer to have them (the canonicalizers(my spell checker
> insists that the word does not exist)) outside of the objects. I
> imagine them just as a special case of crawlers (very similar to what
> crawlers the python AST module supports).
>
> Ronan said:
> How is that different from the existing expression trees? If you really
> have *syntax* trees in mind, then it's not a suitable solution since you
> can't take advantage of the commutativity of addition or of the
> associativity of multiplication (syntactically, "a + b" and "b + a" are
> different things).
> I'm not sure what you mean by canonicaliser, but I'll assume you refer
> to Add(*foo) being used to compute the sum of the elements of foo. I
> agree that it's a problem and a violation of the single-responsibility
> principle. We should replace Add(*foo) with an efficient function
> ssum(foo).
>
> When I say canonicalizer I mean the algorithm that converts eg
> "2*x+1+x+3" to "4+3*x". I don't want commutativity in + or
> associativity in *. I want them to be containers. Then, (just giving
> an **example**, not necessary my vision of the future) when the AST is
> constructed a default canonicalizer is chosen. A way to do this will
> be:
> 0. parse the input and create AST (basically just sympify(..., 
> evaluate=False))
> 1. call the ChoseGoodCanonicalizer crawler on the AST
> 2.a. ChoseGoodCanonicalizer returns canonicalizer implementating of
> our current algorithms for trees containing only symbols
> 2.b. it returns another canonicalizer if the AST tree contains stranger 
> things.
>
> This does not directly fix the problem with two people subclassing
> Expr for different purposes and then trying to mix those two. But
> different canonicalizers can be called only on subtrees. Now as it
> stands this is impossible. If I have MatrixSymbols A and B and ket K I
> can not do A*B*K and expect good results.
>
> I am sure that my lack of education in CS is preventing me to see a
> better solution, that is why I am shooting those ideas here: some of
> you should be able to correct me. But separating container and
> canonicalizer (hopefully I have defined the word in this mail) seems a
> good idea.
>
> Stefan
>
> On 9 March 2012 19:55, Ronan Lamy <ronan.l...@gmail.com> wrote:
>> Le vendredi 09 mars 2012 à 09:50 +0100, Joachim Durchholz a écrit :
>>> Wearing my software architect hat:
>>>
>>> Am 09.03.2012 01:51, schrieb Aaron Meurer:
>>> > So you want to separate the canonicalizers from both the objects and
>>> > the containers?  It seems to me that it would be best to just put them
>>> > in the objects themselves, probably just using the Python double
>>> > dispatch system, but this is an interesting idea too.
>>>
>>>
>>> Multiple dispatch or multiple inheritance?
>>
>> In Python, double dispatch refers to the complicated mechanism by which
>> binary operators get evaluated (e.g. when you have the expression
>> "a + b"). That reminds me that I started to write an explanation of how
>> it really works, I ought to finish it some day. Meanwhile, see
>> http://docs.python.org/library/numbers.html#implementing-the-arithmetic-operations
>>
>> I don't really get Aaron's meaning either, though.
>>
>>>
>>> Multiple dispatch (i.e. selecting the function to be executed based on
>>> the type of more than one parameter) makes extending the system via
>>> subclasses unmodular: people cannot extend independently, they must
>>> coordinate.
>>>
>>> Assuming that X' is a subclass of some class X,
>>> if we have a function f(A,B),
>>> and some person writes A' and defines f(A',B),
>>> and somebody else writes B' and defines f(A,B'),
>>> and a third person wants to use both A' and B', and calls f(A',B'), that
>>> call is ambiguous, it could go to the A' or the B' variant of f.
>>>
>>> In most likelihood, both are wrong since the A' and the B' variants of f
>>> were written because the new subclasses needed new code in f.
>>> So if somebody wants to combine A' and B', he needs to "fill the matrix"
>>> with a new f(A',B').
>>
>> I'm not sure multiple dispatch is the problem. Calling f[X, Y] the
>> implementation of f for instances of the classes X and Y, then if both
>> A' and B' obey Liskov substitutability, f[A', B](a', b') == f[A, B'](a',
>> b') == f[A, B](a', b'), so there is no ambiguity in what the value of
>> f(a', b') should be.
>>
>>>
>>> It would be possible to do this inside SymPy. Just document which
>>> functions are using multiple dispatch, and warn potential subclass
>>> authors that overriding these functions requires coordination with
>>> anybody writing a subclass on another parameter.
>>>
>>>
>>> Multiple inheritance as done in Python is unsound in the presence of
>>> diamond inheritance.
>>>
>>> I have an essay on the topic; see
>>> http://durchholz.org/essays/object-orientation/diamond-inheritance/
>>>
>>> IMNSHO, Python's C3 MRO is better than the original mechanism but still
>>> hopelessly inadequate.
>>> In general, this is not a big problem. However, SymPy would be
>>> particularly vulnerable, since it is dealing with a hierarchy of
>>> mathematical concepts, with abundant diamond structures (actually, the
>>> essay uses hypothetical Group and Monoid classes to explain the
>>> difficulties).
>>
>> A few comments on your essay:
>> * You obviously meant 'Ring' instead of 'Group'.
>> * It doesn't apply to Python: in Python, you can't rename inherited
>> methods and 'a = d' can never mean 'hammer square peg d into round hole
>> a'.
>> * There is a categorical oversight in your analysis: in mathematical
>> terms, 'Monoid' and 'Ring' are categories, while specific algebraic
>> structures (e.g. the ring of integers (ZZ, +, *)) are members of these
>> categories. The instances of the classes you discuss are elements of
>> these structures (e.g. the integer 42), so the classes themselves
>> represent the structures. In Pythonic terms, Monoid and Ring should be
>> metaclasses, which means you don't have a diamond any more, since B and
>> C are instances of the same metaclass Monoid, but don't share a common
>> base class.
>>
>>>
>>>  > Would that make it more or less extensible?
>>>
>>> More extensible, until the mechanisms are in widespread use, then it
>>> will start becoming less extensible.
>>>
>>> My advice would be to stick with
>>> - single dispatch
>>
>> We can't. "a + b" necessarily invokes some form of double dispatch.
>>
>>> - single inheritance
>>> - finding ways to structure the logic so that these two are enough
>>> - where that does not work (and we will have that), program some
>>> explicit mechanism for resolving what needs to be done in what case.
>>>
>> Hm, you're just saying that we should avoid multiple inheritance and
>> multiple dispatch. But the critical question is "How?" Could you take an
>> existing example of multiple inheritance, for instance AtomicExpr, and
>> explain what you think we should with it?
>>
>>>
>>> I hope this has shed some light on the issues.
>>> I'm aware that this were some pretty strong statements on a sometimes
>>> controversial topic. Feel free to ask questions, or to challenge
>>> assumptions and conclusions.
>>>
>>> Regards,
>>> Jo
>>>
>>
>>
>> --
>> You received this message because you are subscribed to the Google Groups 
>> "sympy" group.
>> To post to this group, send email to sympy@googlegroups.com.
>> To unsubscribe from this group, send email to 
>> sympy+unsubscr...@googlegroups.com.
>> For more options, visit this group at 
>> http://groups.google.com/group/sympy?hl=en.
>>
>
> --
> You received this message because you are subscribed to the Google Groups 
> "sympy" group.
> To post to this group, send email to sympy@googlegroups.com.
> To unsubscribe from this group, send email to 
> sympy+unsubscr...@googlegroups.com.
> For more options, visit this group at 
> http://groups.google.com/group/sympy?hl=en.
>

-- 
You received this message because you are subscribed to the Google Groups 
"sympy" group.
To post to this group, send email to sympy@googlegroups.com.
To unsubscribe from this group, send email to 
sympy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sympy?hl=en.

Reply via email to