Using non-ascii symbols

Claudio Grondi claudio.grondi at freenet.de
Fri Jan 27 09:47:15 CET 2006


Bengt Richter wrote:
> On Thu, 26 Jan 2006 17:47:51 +0100, Claudio Grondi <claudio.grondi at freenet.de> wrote:
> 
> 
>>Rocco Moretti wrote:
>>
>>>Terry Hancock wrote:
>>>
>>>
>>>>One thing that I also think would be good is to open up the
>>>>operator set for Python. Right now you can overload the
>>>>existing operators, but you can't easily define new ones.
>>>>And even if you do, you are very limited in what you can
>>>>use, and understandability suffers.
>>>
>>>
>>>One of the issues that would need to be dealt with in allowing new 
>>>operators to be defined is how to work out precedence rules for the new 
>>>operators. Right now you can redefine the meaning of addition and 
>>>multiplication, but you can't change the order of operations. (Witness 
>>>%, and that it must have the same precedence in both multiplication and 
>>>string replacement.)
>>>
>>>If you allow (semi)arbitrary characters to be used as operators, some 
>>>scheme must be chosen for assigning a place in the precedence hierarchy.
>>
>>Speaking maybe only for myself:
>>I don't like implicit rules, so I don't like also any  precedence 
>>hierarchy being in action, so for safety reasons I always write even 
>>8+6*2 (==20) as 8+(6*2) to be sure all will go the way I expect it.
>>
> 
> Maybe you would like the unambiguousness of
>     (+ 8 (* 6 2))
> or
>     6 2 * 8 +
> ?
> 
> Hm, ... ISTM you could have a concept of all objects as potential operator
> objects as now, but instead of selecting methods of the objects according
> to special symbols like + - * etc, allow method selection by rules applied
> to a sequence of objects for selecting methods. E.g., say
>     a, X, b, Y, c
> is a sequence of objects (happening to be contained in a tuple expression here).
> Now let's define seqeval such that
>     seqeval((a, X, b, Y, c))
> looks at the objects to see if they have certain methods, and then calls some of
> those methods with some of the other objects as arguments, and applies rules of
> precedence and association to do something useful, producing a final result.
> 
> I'm just thinking out loud here, but what I'm getting at is being able to write
>     8+6*2
> as
>     seqeval((8, PLUS, 6, TIMES, 2))
> with the appropriate definitions of seqeval and PLUS and TIMES. This is with a view
> to having seqeval as a builtin that does standard processing, and then having
> a language change to make white-space-separated expressions like
>     8 PLUS 6 TIMES 2
> be syntactic sugar for an implicit
>     seqeval((8, PLUS, 6, TIMES, 2))
> where PLUS and TIMES may be arbitrary user-defined objects suitable for seqeval.
> I'm thinking out loud, so I anticipate syntactic ambiguities in expressions and the need to
> use parens etc., but this would in effect let us define arbitrarily named operators.
> Precedence might be established by looking for PLUS.__precedence__. But as usual,
> parens would control precedence dominantly. E.g.,
>    (8 PLUS 6) TIMES 2
> would be sugar for
>    seqeval((seqeval(8, PLUS, 6), TIMES, 2)
> 
> IOW, we have an object sequence expression analogous to a tuple expression without commas.
> I guess generator expressions might be somewhat of a problem to disambiguate sometimes, we'll see
> how bad that gets ;-)
> 
> One way to detect operator objects would be to test callable(obj), which would allow
> for functions and types and bound methods etc. Now there needs to be a way of
> handling UNARY_PLUS vs PLUS functionality (obviously the name bindings are just mnemonic
> and aren't seen by seqeval unless they're part of the operator object). ...
> 
> A sketch:
> 
>  >>> def seqeval(objseq):
>  ...     """evaluate an object sequence. rules tbd."""
>  ...     args=[]
>  ...     ops=[]
>  ...     for obj in objseq:
>  ...         if callable(obj):
>  ...             if ops[-1:] and obj.__precedence__<= ops[-1].__precedence__:
>  ...                 args[-2:] = [ops.pop()(*args[-2:])]
>  ...             ops.append(obj)
>  ...             continue
>  ...         elif isinstance(obj, tuple):
>  ...             obj = seqeval(obj)
>  ...         while len(args)==0 and ops: # unary
>  ...             obj = ops.pop()(obj)
>  ...         args.append(obj)
>  ...     while ops:
>  ...         args[-2:] = [ops.pop()(*args[-2:])]
>  ...     return args[-1]
>  ...
>  >>> def PLUS(x, y=None):
>  ...     print 'PLUS(%s, %s)'%(x,y)
>  ...     if y is None: return x
>  ...     else: return x+y
>  ...
>  >>> PLUS.__precedence__ = 1
>  >>>
>  >>> def MINUS(x, y=None):
>  ...     print 'MINUS(%s, %s)'%(x,y)
>  ...     if y is None: return -x
>  ...     else: return x-y
>  ...
>  >>> MINUS.__precedence__ = 1
>  >>>
>  >>> def TIMES(x, y):
>  ...     print 'TIMES(%s, %s)'%(x,y)
>  ...     return x*y
>  ...
>  >>> TIMES.__precedence__ = 2
>  >>>
>  >>> seqeval((8, PLUS, 6, TIMES, 2))
>  TIMES(6, 2)
>  PLUS(8, 12)
>  20
>  >>> seqeval(((8, PLUS, 6), TIMES, 2))
>  PLUS(8, 6)
>  TIMES(14, 2)
>  28
>  >>> seqeval(((8, PLUS, 6), TIMES, (MINUS, 2)))
>  PLUS(8, 6)
>  MINUS(2, None)
>  TIMES(14, -2)
>  -28
>  >>> seqeval((MINUS, (8, PLUS, 6), TIMES, (MINUS, 2)))
>  PLUS(8, 6)
>  MINUS(14, None)
>  MINUS(2, None)
>  TIMES(-14, -2)
>  28
>  >>> list(seqeval((i, TIMES, j, PLUS, k)) for i in (2,3) for j in (10,100) for k in (5,7))
>  TIMES(2, 10)
>  PLUS(20, 5)
>  TIMES(2, 10)
>  PLUS(20, 7)
>  TIMES(2, 100)
>  PLUS(200, 5)
>  TIMES(2, 100)
>  PLUS(200, 7)
>  TIMES(3, 10)
>  PLUS(30, 5)
>  TIMES(3, 10)
>  PLUS(30, 7)
>  TIMES(3, 100)
>  PLUS(300, 5)
>  TIMES(3, 100)
>  PLUS(300, 7)
>  [25, 27, 205, 207, 35, 37, 305, 307] 
> 
> Regards,
> Bengt Richter
At the first glance I like this concept much and mean it is very 
Pythonic in the sense of the term as I understand it. I would be glad to 
see it implemented if it does not result in any side effects or other 
problems I can't currently anticipate.

Claudio



More information about the Python-list mailing list