Code blocks

D

David Mertz

Back over in that same endless Lisp thread, the idea of codeblocks came
up. Several variations were discussed, with some different opinions on
just how ugly or bearable the syntax was (I don't think any of them
registered as "wonderful" by anyone).

I think that codeblocks, a la Ruby, Smalltalk, xBase, would be kinda
nice to have. But the thing I like about them isn't really that they
can be inlined, but rather simply that the operate without defining a
new (nested) scope. Basically, codeblocks just amount to
boilerplate--except possibly paramaterized... and shorter, of course.

So for that, I would suggest a new scopeless pseudo-function syntax.
E.g.:

block foo(this, that, other):
x = this+that
y = this*other
z = x // (y-that)

Using this is like a function, but the names wind up in the calling
namespace:

def bar(someblock):
a, b, c = (4, 5, 6)
someblock(a,b,c)
return x,y,z

vals = bar(foo) #-> (9, 24, 0)

That is, I have no desire for blocks to be nameless, just for them to be
passable as first class objects.

Yours, David...
 
A

Alex Martelli

David Mertz wrote:
...
So for that, I would suggest a new scopeless pseudo-function syntax.
E.g.:

block foo(this, that, other):
x = this+that
y = this*other
z = x // (y-that)

Using this is like a function, but the names wind up in the calling
namespace:

Calling rather than defining? For some reason this seems strange
to me. Maybe it's just the vague memory of dynamic scoping being
a rather bad thing, but aren't there more use cases for affecting the
defining scope (which you DO know intimately when you're defining)
than for defining the calling scope (which in general you'd treat as a
black box?
def bar(someblock):
a, b, c = (4, 5, 6)
someblock(a,b,c)
return x,y,z

vals = bar(foo) #-> (9, 24, 0)

That is, I have no desire for blocks to be nameless, just for them to be
passable as first class objects.

Yes, renouncing namelessness could perhaps be a reasonable compromise.
But let's consider use cases -- e.g. a "with this file" HOF. If a block's
variables were in the _defining_ scope, I could change the current:

def blahblah(filename):
f = open(filename)
try:
x = someprocessingwith(f)
y = somemorewith(f)
finally:
f.close()
# proceed, using x and y

into:

def blahblah(filename):
block computexy(f):
x = someprocessingwith(f)
y = somemorewith(f)
do_with_file(filename, computexy)
# proceed, using x and y

using do_with_file as a black-box HOF, though it would typically be

def do_with_file(filename, thecallable):
f = open(filename)
try:
return thecallable(f)
finally:
f.close()

and similarly for doing things while holding a lock, etc, etc. Basically,
the kind of things you do with code-block passing in Smalltalk, Ruby &c.

What different set of interesting use-cases would be enabled by
having the block's apparently-local variables live in the namespace
of the caller rather in that of the definer?


Alex
 
S

Simon Burton

On Mon, 13 Oct 2003 16:31:27 -0400, David Mertz wrote:

....
So for that, I would suggest a new scopeless pseudo-function syntax.
E.g.:

block foo(this, that, other):
x = this+that
y = this*other
z = x // (y-that)

Using this is like a function, but the names wind up in the calling
namespace:

def bar(someblock):
a, b, c = (4, 5, 6)
someblock(a,b,c)
return x,y,z

vals = bar(foo) #-> (9, 24, 0)

That is, I have no desire for blocks to be nameless, just for them to be
passable as first class objects.

Yours, David...

Yeah, right on!
I often hack code realtime, and wish i could name a chunk of code
without having to return/accept huge (and often changing) tuples of stuff...

Anyway, now that i think about this, it should be possible to lookup
the calling context's locals and insert the current locals. Then
wrap this in a function, "expose", say. One could even do this with
the arguments and write an "absorb" function:


def foo():
absorb()
x = this+that
y = this*other
z = x // (y-that)
expose()

Then maybe later on when the code has cooled down sufficiently, one could
revert to the more informative args/return mechanics.

Simon.
 
D

David Mertz

|> block foo(this, that, other):
|> x = this+that
|> y = this*other
|> z = x // (y-that)
|> def bar(someblock):
|> a, b, c = (4, 5, 6)
|> someblock(a,b,c)
|> return x,y,z
|> vals = bar(foo) #-> (9, 24, 0)

|Anyway, now that i think about this, it should be possible to lookup
|the calling context's locals and insert the current locals.
| def foo():
| absorb()
| x = this+that
| y = this*other
| z = x // (y-that)
| expose()

It's not too hard to look up the call stack to GET the binding from the
calling context. I.e.
... import inspect
... print inspect.currentframe(1).f_locals
... ... x, y, z = 1, 2, 3
... about_caller()
... {'y': 2, 'x': 1, 'z': 3}

Actually, you could define this in a utility 'absorb()' function (which
would look at 'currentframe(2)' instead of 1). The problem is that
there's no good way to put values back INTO the calling context. So I
can't see a way to write the 'expose()' function.

Btw. In answer to Alex' concern about dynamic scoping--which admittedly
my suggestion basically amounts to: I think the main use of a "block"
(or whatever name might be used) is that certain code might be used
between many local contexts. If you just define a block within a
particular function 'spam()', that hardly helps you when you want to do
the same steps in 'eggs()'.

In a way, a block amounts to a poor-man's macro, since it IS an
expansion of sorts. But to my mind, it isn't nearly as dangerous as
macros can be. Then again, as I proposed it, it might be non-obvious
when a call is to a block versus when it is to a function. So maybe
some special syntax would be needed at the point of the call also.

Of course, as I envision it, a block might also do a bit of inspection
of its context (as in the 'about_caller()' function above)... so maybe
it really is skating too close to the perils of macros, which Alex and I
pretty much agree on.

Yours, David...
 
C

Christos TZOTZIOY Georgiou

I think that codeblocks, a la Ruby, Smalltalk, xBase, would be kinda
nice to have. But the thing I like about them isn't really that they
can be inlined, but rather simply that the operate without defining a
new (nested) scope. Basically, codeblocks just amount to
boilerplate--except possibly paramaterized... and shorter, of course.

I had such an idea too, perhaps could be useful.

The definition syntax I imagined was:
<block_name>:
# do something here
pass

and running the code block was:
<block_name>

that is, without the colon at the end.
No scopes added, just a kind of code compilation and 'exec' use with
syntactic sugar, so they could represent plain code objects.
The block names are definitely local in a function (so the code snippet
/ block could do LOAD_FAST for the local variables it references), and
no arguments would be allowed.

That was my idea, but parts of it can be changed.

For more info about implementation proposal, see:
http://groups.google.com/[email protected]
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,990
Messages
2,570,211
Members
46,796
Latest member
SteveBreed

Latest Threads

Top