On Wed, 26 Mar 2014 00:30:21 -0400, Terry Reedy wrote: > On 3/25/2014 8:12 PM, Steven D'Aprano wrote: >> On Tue, 25 Mar 2014 19:55:39 -0400, Terry Reedy wrote: >> >>> On 3/25/2014 11:18 AM, Steven D'Aprano wrote: >>> >>>> The thing is, we can't just create a ∑ function, because it doesn't >>>> work the way the summation operator works. The problem is that we >>>> would want syntactic support, so we could write something like this: >>>> >>>> p = 2 >>>> ∑(n, 1, 10, n**p) >>> >>> Of course we can. If we do not insist on separating the dummy name >>> from the expression that contains it. this works. >>> >>> def sigma(low, high, func): >>> sum = 0 >>> for i in range(low, high+1): >>> sum += func(i) >>> return sum >> >> There is no expression there. There is a function. >> >> You cannot pass an expression to a function in Python, > > One passes an unquoted expression in code by quoting it with either > lambda, paired quote marks (Lisp used a single '),
Passing *strings* and *functions* is not the same as having compiler support for delayed evaluation. At best its a second-class work-around. Contrast: def if_else(true_function, condition, false_function): if condition: return true_function() else: return false_function() if_else(lambda: x/0, x != 0, lambda: float("inf")) with this: x/0 if x != 0 else float("inf") Aside from the difference between the function form and operator form, the second case is much more direct and natural than the first. > or using it in a form > that implicitly quotes it (that includes def statements). Unquoted > expressions in statements ultimately get passed to an internal > functions. I think you are mistaken. Take the unquoted expression `x+1`. It doesn't get passed to anything, it gets compiled into byte code and evaluated: py> from dis import dis py> dis("x+1") 1 0 LOAD_NAME 0 (x) 3 LOAD_CONST 0 (1) 6 BINARY_ADD 7 RETURN_VALUE Perhaps you are thinking of one or two special cases, such as list comps: py> dis("[x+1 for x in spam]") 1 0 LOAD_CONST 0 (<code object <listcomp> at 0xb7af22a0, file "<dis>", line 1>) 3 LOAD_CONST 1 ('<listcomp>') 6 MAKE_FUNCTION 0 9 LOAD_NAME 0 (spam) 12 GET_ITER 13 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 16 RETURN_VALUE But that's an implementation detail, not a language requirement, and it doesn't apply to all delayed expressions: py> dis("1/x if x else y") 1 0 LOAD_NAME 0 (x) 3 POP_JUMP_IF_FALSE 14 6 LOAD_CONST 0 (1) 9 LOAD_NAME 0 (x) 12 BINARY_TRUE_DIVIDE 13 RETURN_VALUE >> 14 LOAD_NAME 1 (y) 17 RETURN_VALUE Some Python implementations may use internal functions under the hood to delay the evaluation of an expression, but you still need support from the interpreter to compile the expression as an internal function rather than evaluating it. > > not in the sense I am talking about, > > well, if you eliminate all the possibilities ... Obviously you can pass an expression to a function in the trivial sense that you can put an expression inside a function call. But that is not what I am talking about, since the expression is evaluated first, then the function is called: py> dis("function(spam + eggs*cheese)") 1 0 LOAD_NAME 0 (function) 3 LOAD_NAME 1 (spam) 6 LOAD_NAME 2 (eggs) 9 LOAD_NAME 3 (cheese) 12 BINARY_MULTIPLY 13 BINARY_ADD 14 CALL_FUNCTION 1 (1 positional, 0 keyword pair) 17 RETURN_VALUE I'm referring to delaying execution of the expression until later. Coincidentally, the Sum function we were discussing is a notable example of Jensen's Device: https://en.wikipedia.org/wiki/Jensen%27s_device which is effectively what I'm referring to. > > because expressions are not first-class objects. > > The concept is not a class, and the Python stdlib does not have an > expression class. But people have written classes to represent the > concept. I used existing classes instead. > > Expressions are not (normally) mathematical objects either. (An > exception is rewriting theory, or other theories, where strings > representing expressions or WFFs (well-formed formulas) are the only > objects.) Mathematicians quote expression strings by context or > positiion. The sigma form is one of many examples. Hmmm. I think you are misunderstanding me, possibly because I wrote "first-class object" when I should have called it a "first-class value". Sorry. I'm not necessarily referring to creating something like an "arithmetic expression class" with methods and attributes. For example, sympy has things like that, so you can perform symbolic operations on the expression. That's not what I mean. What I mean is that you, the programmer, writes down an ordinary Python expression, using ordinary expression syntax, and the compiler treats it as a value in and of itself, rather than evaluating it to find out what value it has. In Python this will probably be some sort of object, but that's not the important part. The important part is that it is a *value*. (Compare to languages where functions are not first-class values. You cannot pass a function to another function, or stick them in a list, or create them on the fly. You can only call them, evaluating them immediately.) In Algol60, the compiler used thunks, which are a type of closure; in CPython, list comps use a hidden function object; the ternary if compiles byte code for a test and jump. In case you haven't read the article on Jensen's Device above, in Algol60 you can write a Sum function that behaves as a mathematician would expect. For example, to sum the entries of an array V for indexes 1 through 100, a mathematician might write it something like this: 10 ∑ V[i] i=1 which we can rearrange to function-call syntax like this: Sum(i, 1, 100, V[i]) In Algol60, this function call would: - pass the name "i" (not a string!) as the first argument; - pass 1 as the second argument; - pass 100 as the third argument; - pass the expression "V[i]" (not a string!) as the fourth argument and then *inside* the function Sum the expressions "i" and "V[i]" can be evaluated or assigned to as needed. Using Python syntax rather than Algol syntax: def Sum(name, lower, upper, expression): total = 0 for name in range(lower, upper+1): total += expression return total This doesn't work in Python! Python lacks call-by-name semantics, so the function call would: - evaluate the expression i, and pass that value as the first argument; - pass 1 as the second argument; - pass 100 as the third argument; - evaluate V[i] and pass that value as the fourth argument and then inside the function Sum "name" refers to the local variable name, not the caller's variable i. Likewise "expression" refers to a local variable, and not the caller's expression V[i]. Python has no general way of doing this. There are a few ad-hoc special cases, like list comps, the ternary if operator, etc. which are hard- coded in the compiler to delay execution of expressions. For the rest, you have a choice: - give up on delayed evaluation, and redesign your API; or - manually manage the delayed evaluation yourself, using some combination of functions, eval or exec. -- Steven D'Aprano http://import-that.dreamwidth.org/ -- https://mail.python.org/mailman/listinfo/python-list