Delayed evaluation of expressions [was Re: Time we switched to unicode?]

Rustom Mody rustompmody at gmail.com
Wed Mar 26 18:32:04 CET 2014


On Wednesday, March 26, 2014 9:35:53 PM UTC+5:30, Steven D'Aprano wrote:
> 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:

Once the language has lambda, most else can be fashioned
See the classic papers 
lambda the ultimate imperative
lambda the ultimate declarative
lambda the ultimate goto
at here http://library.readscheme.org/page1.html

> 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.

Heres Jensen device in python
First your pseudo algol version with python frills like range removed

def sumjensen(i,lower,upper,exp):
# i and exp by name
# i get and set, exp only get required
    tot = 0
    i = lower
    while i <= upper:
        tot += exp
        i = i + 1
    return tot

Now actual python

def sumjensen(i_get, i_set,lower,upper,exp):
    tot = 0
    i_set(lower)
    while i_get() <= upper:
        tot += exp_get()
        i_set(i_get() + 1)
    return tot


i=0
a=[3,4,5]
i_get = lambda : i
def i_set(val):
   global i
   i = val

exp_get = lambda : a[i_get()]


call as sumjensen(i_get, i_set, lower, upper, exp_get)

[Note that because of lambda's restriction to being only an expression
I have to make it a def because of need for  global]



More information about the Python-list mailing list