Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

On PEP 312: simple implicit lambda

2 views
Skip to first unread message

Christos TZOTZIOY

unread,
Feb 16, 2003, 9:47:11 AM2/16/03
to
A little far-fetched, so please bear with me.

I would suggest that 'lambda-less lambdas' become code snippets instead
of actual functions... by code snippets, I mean something like
subroutines in the old BASIC way. The reason for this is only
performance: avoiding a function call. Read the seemingly off-topic
post and at the end I will make the connection to implicit lambdas.

Before bashing me for referring to BASIC, please note that there are
many places where we have to duplicate code; I know that the duplicate
code can be converted to a function call, but at the cost of losing
access to the local variables.

Take for example:

data = file_object.read(4096)
while data:
# process here
data = file_object.read(4096)

(In this simple case, the need to avoid duplication is not that strong,
so it is a bad example, but it's just an idea that popped up in my mind
a few minutes ago; I won't stand up to it if proven to be stupid :)
There are cases with if...elif...else constructs where duplicate code is
used too, so if needed, I will find more examples.)

What I would do in such a case, would be the following:

<read_more_data>:
data = file_object.read(4096)
while data:
# process here
<read_more_data>

The syntax I imagined is the one proposed above, with angle brackets and
all. What happens is this:

1. The compiler finds a left angle bracket, followed by a valid
identifier, followed by a right angle bracket, folllowed by a colon.
The bytecode of the indented following statements is stored somewhere
else, probably appended at the end of the normal code object. The
compiled bytecode ends with a RESUME opcode (not existant now). In
place of the whole construct, the compiler issues a SNIPPET <address>
opcode (also not existant), which <address> will be filled in at the end
of the compilation cycle, kind of like JUMP_FORWARD arguments. The
SNIPPET opcode, when executed, pushes the current "program counter" on
the stack, and uses the snippet address as the new "program counter".
The RESUME opcode pops the return address and stores it in the "program
counter". No frame fiddling.
So at the point of definition, at runtime the code gets executed as
previously.

2. When the compiler finds a '<', identifier, '>' construct at the
beginning of a statement without a colon appended, it issues a SNIPPET
bytecode to the address of the code snippet.

3. The compiler does not allow duplicate definitions for a code snippet
identifier in the same namespace. This is to avoid bugs.

4. The snippet identifiers are allowed and valid only inside the code
block they are defined in (eg a code snippet defined in a function is
unavailable to other functions, except for ones defined inside it).

5. Code snippets are not macros. Plus, they should never be augmented
to accept any arguments.

6. Standalone code path alteration statements like 'continue', 'break',
'raise' should not be valid in a code snippet.

7. If the compiler sees a ':' at the beginning of an expression, the
following expression is stored and called as an unnamed code snippet
(just to avoid the function call overhead of a lambda). This mechanism
cannot be used for deferred evaluation, though.

Now you can bash me to death :)
--
TZOTZIOY, I speak England very best,
Real email address: 'dHpvdEBzaWwtdGVjLmdy\n'.decode('base64')

Andrew Bennetts

unread,
Feb 16, 2003, 10:32:04 AM2/16/03
to
On Sun, Feb 16, 2003 at 04:47:11PM +0200, Christos TZOTZIOY Georgiou wrote:
>
> Take for example:
>
> data = file_object.read(4096)
> while data:
> # process here
> data = file_object.read(4096)
>
> (In this simple case, the need to avoid duplication is not that strong,
> so it is a bad example, but it's just an idea that popped up in my mind
> a few minutes ago; I won't stand up to it if proven to be stupid :)
> There are cases with if...elif...else constructs where duplicate code is
> used too, so if needed, I will find more examples.)

Why not simply:

data = 'dummy'
while data:
data = file_object.read(4096)
# process here

This avoids the duplication quite neatly.


> What I would do in such a case, would be the following:
>
> <read_more_data>:
> data = file_object.read(4096)
> while data:
> # process here
> <read_more_data>

You can do this already:

def read_more_data():
return file_object.read(4096)
data = read_more_data()


while data:
# process here

data = read_more_data()

Slightly different, but doesn't require any new features with gratuitous
punctuation. I still prefer my earlier alternative.

A much less radical solution than yours would be if Python supported full
closures, i.e. being able to bind variables in outer scopes from a nested
function, then you could do:

def read_more_data():
outer data # By analogy with the global statement
data = file_object.read(4096)
read_more_data()


while data:
# process here

read_more_data()

Of course, my fictitious "outer" statement would be problematic with
multiple levels of function nesting... I'm not sure that "outer outer outer foo"
is a construct people want to see, although I suspect it'd be more popular
than your proposal.

Do you have any other examples where you think this feature would be useful?

-Andrew.


Dan Schmidt

unread,
Feb 16, 2003, 12:15:05 PM2/16/03
to
Andrew Bennetts <andrew-p...@puzzling.org> writes:

| On Sun, Feb 16, 2003 at 04:47:11PM +0200, Christos TZOTZIOY Georgiou wrote:
||
|| Take for example:
||
|| data = file_object.read(4096)
|| while data:
|| # process here
|| data = file_object.read(4096)
||
|| (In this simple case, the need to avoid duplication is not that strong,
|| so it is a bad example, but it's just an idea that popped up in my mind
|| a few minutes ago; I won't stand up to it if proven to be stupid :)
|| There are cases with if...elif...else constructs where duplicate code is
|| used too, so if needed, I will find more examples.)
|
| Why not simply:
|
| data = 'dummy'
| while data:
| data = file_object.read(4096)
| # process here
|
| This avoids the duplication quite neatly.

But it doesn't do the same thing, as it does the processing a final
time even when data becomes false. The following code does do the
same as the original:

while True:
data = file_object.read(4096)
if not data:
break
# process here

but it's a little awkward; it's kind of weird that we really do want
to have a while loop with data not being false as the condition, but
are forced to instead make the condition be True and break out in the
middle.

In Python, to code these 'loop and a half' constructs, you have to
either duplicate some code, as Christos did, or put a conditional
break halfway through the loop, as I did.

Dan

--
http://www.dfan.org

Alex Martelli

unread,
Feb 16, 2003, 12:23:23 PM2/16/03
to
Dan Schmidt wrote:
...

> In Python, to code these 'loop and a half' constructs, you have to
> either duplicate some code, as Christos did, or put a conditional
> break halfway through the loop, as I did.

Or (generally best these days) define and use an iterator, e.g.:

def readblocks(fileobject, blocksize=4096):
def readoneblock():
return fileobject.read(blocksize)
return iter(readoneblock, '')


for data in readblocks(file_object):
process(data)

or, use a lambda as iter's first argument to keep the function
you're introducing anonymous, if you prefer that style:

for data in iter(lambda:file_object.read(4096), ''):
process(data)


Alex

Manuel M. Garcia

unread,
Feb 16, 2003, 12:56:15 PM2/16/03
to
On Sun, 16 Feb 2003 16:47:11 +0200, Christos "TZOTZIOY" Georgiou
<DLNXPE...@spammotel.com> wrote:
(edit)

>Take for example:
>
>data = file_object.read(4096)
>while data:
> # process here
> data = file_object.read(4096)
(edit)

><read_more_data>:
> data = file_object.read(4096)
>while data:
> # process here
> <read_more_data>

The usual idiom used in this case is:

while 1:


data = file_object.read(4096)
if not data: break
# process here

I agree it is not perfect. I never prefer an infinite loop, and I
always try my best to put the loop termination condition as part of
the 'while' statement.

Here is another Python trick inside of a little test program:

# ###################################### #
# tzotzioy_loop01.py

import sys

def store_data(f):
"""adds a .data attribute to a function to
store the last result"""
class _:
def __call__(self):
self.data = f()
return self.data
return _()

file_object = file('tzotzioy_loop01.py')
_readline = store_data( file_object.readline )
i = 0
while _readline():
i += 1
sys.stdout.write('%03i %s' % (i, _readline.data))

# ###################################### #

Using the trick of a temporary class, you have a good place to store
'.data' for later use if the function call is sucessful.

Manuel

Manuel M. Garcia

unread,
Feb 16, 2003, 1:51:28 PM2/16/03
to
On Sun, 16 Feb 2003 16:47:11 +0200, Christos "TZOTZIOY" Georgiou
<DLNXPE...@spammotel.com> wrote:
(edit)
>A little far-fetched, so please bear with me.
>
>I would suggest that 'lambda-less lambdas' become code snippets instead
>of actual functions... by code snippets, I mean something like
>subroutines in the old BASIC way. The reason for this is only
>performance: avoiding a function call. Read the seemingly off-topic
>post and at the end I will make the connection to implicit lambdas.

I forgot to mention that on Python-Dev there is some talk about adding
the ability to manipulate blocks of code, to allow extensions to the
language that are written in Python. Beyond 'lambda-less lambdas', it
would also allow the creation of new looping and control statements!

It would introduce a new kind of object in Python, called a 'thunk'.
A 'thunk' would be a block of Python code packaged inside of an
object. The 'thunk' could then be programmatically manipulated and
its execution controlled.

I believe Guido is a little envious of Ruby's ability to do stuff like
this...

>From: gu...@python.org (Guido van Rossum)
>Date: Fri, 31 Jan 2003 13:45:21 -0500
>Subject: question??? [Python-Dev] Property syntax
>
>> From: "Guido van Rossum" <gu...@python.org>
>> >
>> > So the compiler cannot look at what the thunk is used for. We need
>> > uniform treatment of all thunks. (Even if it means that the thunk's
>> > consumer has to work a little harder.)
>>
>> is it a correct assumption that generalized thunks be it, and so
>> argumenting against them is wasting my time?
>
>Not at all. This is still wide open. I happen to like generalized
>thunks because they remind me of Ruby blocks. But I realize there are
>more ways to skin this cat. Keep it coming!
>
>And remember, I'm arguing in my spare time. :-)
>
>--Guido van Rossum (home page: http://www.python.org/~guido/)

The discussion went over my head very early on. Unfortunately, the
discussion lost some steam because of the evil influence of the
'if-then-else-expression' controversy. There is currently no good
summary of all the 'thunks' ideas suggested.

The discussion of 'thunks' is very blue sky. 'Thunks' are much more
disruptive than adding a 'if-then-else-expression'! Doubtful we will
ever see them, and if we do see them, it will not be soon.

Manuel

Samuele Pedroni

unread,
Feb 16, 2003, 2:46:20 PM2/16/03
to

"Manuel M. Garcia" <ma...@manuelmgarcia.com> ha scritto nel messaggio
news:jekv4vg5krbk4tois...@4ax.com...

> The discussion went over my head very early on. Unfortunately, the
> discussion lost some steam because of the evil influence of the
> 'if-then-else-expression' controversy.

No, Guido frozen it. It was quite unfocused, IMO too much.

> There is currently no good
> summary of all the 'thunks' ideas suggested.
>

Guido has been provided with a tentative one, but AFAIK he has'nt had yet
the time to read it. Also the python-dev summarizer has received it, so then
maybe in next python-dev summary ...

regrds.


Christos TZOTZIOY

unread,
Feb 16, 2003, 6:39:10 PM2/16/03
to
On Sun, 16 Feb 2003 17:56:15 GMT, rumours say that Manuel M. Garcia
<ma...@manuelmgarcia.com> might have written:

>The usual idiom used in this case is:
>
> while 1:
> data = file_object.read(4096)
> if not data: break
> # process here

Yes, I use that too.

>I agree it is not perfect. I never prefer an infinite loop, and I
>always try my best to put the loop termination condition as part of
>the 'while' statement.

The iter function with two arguments is often a nice help for loop
conditions...

>Here is another Python trick inside of a little test program:

<snip of a function with an internal class>

Interesting, but I find it too fussy. I'd still prefer
'loop-and-a-half' to it.

Thanks for your time, anyway :)

Stuart D. Gathman

unread,
Feb 18, 2003, 10:36:00 PM2/18/03
to
On Sun, 16 Feb 2003 09:47:11 -0500, Christos TZOTZIOY Georgiou wrote:


> What I would do in such a case, would be the following:
>
> <read_more_data>:
> data = file_object.read(4096)
> while data:
> # process here
> <read_more_data>
>
> The syntax I imagined is the one proposed above, with angle brackets and
> all. What happens is this:

I did a much cleaner syntax of this idea for an assembler preprocessor.
Here is what it would look like in Python:

while when:
data = file_object.read(4096)
then data:
# process here

The idea is that WHEN introduces arbitrary code which "sets up" for
testing a condition, and THEN gets back to testing the condition. When
can also be used with 'if', and is useful when combined with short circuit
evaluation. Here is an example of the assembler code (IBM Series/1). The
WHEN cause lets me issue multiple statements to setup for the CFNEN (like
Intel "string" insns). The DO R6 provides an upper bound on the number of
loop iterations (an error checking feature nearly as useful as bounds
checking).

DO R6,WHILE,WHEN,REG=R6
MVWS BTCBLBUF,R5 COULD PSD AND USE R1 FOR THIS
MVW R4,R2
MVW R3,R7
CFNEN (R5),(R2)
THEN LLT
AW DLEN,R4 POINT R4 TO NEXT RECORD
ENDDO

One final asm example to show how WHEN is used with short circuiting
AND/OR:

IF (BTCBFLAG,OFF,BTCB@MWA+BTCB@MOV,TWI)
CALL BTBUF,(BTCBNODE,0)
ELSEIF (BTCBFLAG,ON,BTCB@MOV,TWI),OR,WHEN
CALL DISPOSE,TYPE=X
MVA BTCBNODE,R4 TRY TO AVOID RETRACE;
CALL GETBLK,TYPE=X GET BLOCK LAST SEEN IN
MVWS R3,BTCBABUF AND CHECK:
MVW R3,#NODE
THEN (BTROOT,NE,BTCBROOT,CD),OR SAME FILE
THEN (R4,P,BTSON,MVD),OR,WHEN LEAF NODE
IF (R4,Z,BTCOUNT,MVWS),THEN,POSTEOF NON EMPTY
THEN (R7,NP,BTCBSLOT,MVWS),OR,(R7,GT,R4),OR,WHEN VALID POS
CALL CHKKEY,TYPE=J MATCHING KEY
THEN NE,THEN
CALL BTTRACE,(BTCBKLEN) ELSE, RETRACE
ENDIF

The (op1,cond,op2,opcode) construct was short for
WHEN opcode op2,op1
THEN cond

In python syntax, that would be:

if (btcb.flag & (BTCB.MWA | BTCB.MOV)) != 0:
btbuf(btcb.node,None)
elif (btcb.flag & BTCB.MOV) != 0 or when:
dispose()
btcb.abuf = node = getblk(btcb.node)
then btroot != btcb.root or btson > 0 or when:
r4 = btcount
if r4 == 0: raise EOFException
r7 = btcb.slot
then r7 <= 0 or r7 > r4 or when:
rc = chkkey() # OK, this is artificial to mirror the ASM code
then rc:
bttrace(btcb.klen)

Flame away! I've always liked it because it allows any sequence of statements,
including nested loops and conditionals, to setup conditions in loops and
short-circuit boolean expressions.

--
Stuart D. Gathman <stu...@bmsi.com>
Business Management Systems Inc. Phone: 703 591-0911 Fax: 703 591-6154
"Confutatis maledictis, flamis acribus addictis" - background song for
a Microsoft sponsored "Where do you want to go from here?" commercial.

Christos TZOTZIOY

unread,
Feb 19, 2003, 2:00:21 AM2/19/03
to
On Wed, 19 Feb 2003 03:36:00 GMT, rumours say that "Stuart D. Gathman"
<stu...@bmsi.com> might have written:

>On Sun, 16 Feb 2003 09:47:11 -0500, Christos TZOTZIOY Georgiou wrote:
>
>
>> What I would do in such a case, would be the following:
>>
>> <read_more_data>:
>> data = file_object.read(4096)
>> while data:
>> # process here
>> <read_more_data>

>I did a much cleaner syntax of this idea for an assembler preprocessor.
>Here is what it would look like in Python:
>
> while when:
> data = file_object.read(4096)
> then data:
> # process here
>
>The idea is that WHEN introduces arbitrary code which "sets up" for
>testing a condition, and THEN gets back to testing the condition.

<snip>

>Flame away! I've always liked it because it allows any sequence of statements,
>including nested loops and conditionals, to setup conditions in loops and
>short-circuit boolean expressions.

I see no need to flame; after all, this is kind of brainstorming (or
idle talk :) on a newsgroup; your syntax is a nice idea for
loops-and-a-half.

Greg Ewing (using news.cis.dfn.de)

unread,
Feb 19, 2003, 7:29:38 PM2/19/03
to
Stuart D. Gathman wrote:

> if (btcb.flag & (BTCB.MWA | BTCB.MOV)) != 0:
> btbuf(btcb.node,None)
> elif (btcb.flag & BTCB.MOV) != 0 or when:
> dispose()
> btcb.abuf = node = getblk(btcb.node)
> then btroot != btcb.root or btson > 0 or when:
> r4 = btcount
> if r4 == 0: raise EOFException
> r7 = btcb.slot
> then r7 <= 0 or r7 > r4 or when:
> rc = chkkey() # OK, this is artificial to mirror the ASM code
> then rc:
> bttrace(btcb.klen)


This is an... er... interesting idea, although I'm
not sure about the choice of the words "when" and
"then". I found I had to twist my brain somewhat to
get the above to read fluidly.

I had an idea of my own some time ago for a loop-
and-a-half construct which goes like this:

while:
<statements>
gives <condition>:
<more statements>

Maybe our ideas could be combined by substituting
"doing" and "gives" for your "when" and "then".
Your example would then become

if (btcb.flag & (BTCB.MWA | BTCB.MOV)) != 0:
btbuf(btcb.node,None)

elif (btcb.flag & BTCB.MOV) != 0 or doing:


dispose()
btcb.abuf = node = getblk(btcb.node)

gives btroot != btcb.root or btson > 0 or doing:


r4 = btcount
if r4 == 0: raise EOFException
r7 = btcb.slot

gives r7 <= 0 or r7 > r4 or doing:
rc = chkkey()
gives rc:
bttrace(btcb.klen)

--
Greg Ewing, Computer Science Dept,
University of Canterbury,
Christchurch, New Zealand
http://www.cosc.canterbury.ac.nz/~greg

Stuart D. Gathman

unread,
Feb 19, 2003, 9:46:44 PM2/19/03
to
On Wed, 19 Feb 2003 19:29:38 -0500, Greg Ewing (using news.cis.dfn.de)
wrote:


> Maybe our ideas could be combined by substituting "doing" and "gives"
> for your "when" and "then". Your example would then become
>
> if (btcb.flag & (BTCB.MWA | BTCB.MOV)) != 0:
> btbuf(btcb.node,None)
> elif (btcb.flag & BTCB.MOV) != 0 or doing:
> dispose()
> btcb.abuf = node = getblk(btcb.node)
> gives btroot != btcb.root or btson > 0 or doing:
> r4 = btcount
> if r4 == 0: raise EOFException
> r7 = btcb.slot
> gives r7 <= 0 or r7 > r4 or doing:
> rc = chkkey()
> gives rc:
> bttrace(btcb.klen)

Yes, this does indeed capture the principle. I'm not going to quibble
over keywords. Your keywords are perfectly understandable. If this is
ever added to python, we can all vote on the keywords.

Another advantage of when/then/doing/gives is that break and continue are
no longer needed. (Although labeled break/continue ala Java can do still
more.) I'm not suggesting that we remove break and continue. I haven't
tried to prove it yet, but I suspect that doing/gives and break/continue
provide exactly the same power to control loops and conditionals without
duplicating code. (Assuming that an infinite loop doesn't count as
duplicate code. Is while 1: the most efficient Python infinite loop?) I
find that doing/gives (I'm starting to like it better than when/then) is
easier to construct from real world conditions. I can then translate to
break/continue forms for conventional languages. (And a mechanical
translation is, of course, where a proof of equivalence would start.)

A disadvantage of doing/gives is that by deeply nesting additional control
structures within a doing clause (yes, you can put loops, with their own
doing/gives clause within a doing clause), the art of obfuscation reaches new
heights. :-)

0 new messages