A critic of Guido's blog on Python's lambda

K

Ken Tilton

I am pretty much ignorant of Common Lisp, but I have the impression
they are the
same as Scheme parameters, i.e. thread-local dynamically scoped
variables
(feel free to correct me if I am mistaken). If I am right, here is how
you would emulate them in recent versions of Python:

import threading, time

special = threading.local()
special.x = 0

def getx():
return special.x

def set_x(value):
special.x = value
time.sleep(3-value) # thread-2 completes after thread-1
print "%s is setting x to %s" % (threading.currentThread(), getx())

if __name__ == '__main__':
print getx() # => 0
threading.Thread(None, lambda : set_x(1)).start() # => 1
threading.Thread(None, lambda : set_x(2)).start() # => 2
time.sleep(3)
print getx() # => 0

Michele Simionato

I was not thinking about the thread issue (of which I know little). The
big deal for Cells is the dynamic bit:

(let ((*dependent* me))
(funcall (rule me) me))

Then if a rule forces another cell to recalculate itself, *dependent*
gets rebound and (the fun part) reverts back to the original dependent
as soon as the scope of the let is exited.

In this case, because my code is so brilliant <g>, there is only one
place in the code where a rule gets called, so it would be easy enough
to manage by saving/restoring the existing value around storing a new
value there.

I do wonder what would happen to Cells if I ever want to support
multiple threads. Or in a parallel processing environment.

kenny


--
Cells: http://common-lisp.net/project/cells/

"Have you ever been in a relationship?"
Attorney for Mary Winkler, confessed killer of her
minister husband, when asked if the couple had
marital problems.
 
J

Joe Marshall

Pisin said:
Is this a Slippery Slope fallacious argument?
(http://c2.com/cgi/wiki?SlipperySlope)

"if python required you to name every function then soon it will
require you to name every number, every string, every immediate result,
etc. And we know that is bad. Therefore requiring you to name your
function is bad!!!! So Python is bad!!!!"

No, it is a question about consistency. Why are functions singled out?

Or is this an attempt to apply an Argumentum ad Logicam fallacy?
 
J

Joe Marshall

Alex said:
I think it's reasonable to make a name a part of functions, classes and
modules because they may often be involved in tracebacks (in case of
uncaught errors): to me, it makes sense to let an error-diagnosing
tracebacks display packages, modules, classes and functions/methods
involved in the chain of calls leading to the point of error _by name_.

I think it's reasonable to make a name a part of types for a different
reason: new types are rarely meant to be used "just once"; but also, if
during debugging any object is displayed, it's nice to be able to show,
as part of the display, "this object is of type X and ...", with X shown
as a name rather than as a complete (thus lengthy) description. (any
decent interactive shell/debugger will let you drill down into the
details as and when you need to, of course, but a well-chosen name can
be often sufficient during such interactive exploration/debugging
sessions, and therefore save time and effort).

I agree that symbolic debugging info is a good thing.
Indeed, "given an object, how do I get its NAME" (for inspection and
debugging purposes) is the most frequently asked question on
comp.lang.python, and I've grown a bit tired of answering "you can't, an
object in general intrinsically ``has no name'', it might have many or
none at all, blah blah" -- yeah, this is technically true (in today's
Python), but there's no real reason why it should stay that way forever
(IMHO). If we at least ALLOWED named objects everywhere, this would
further promote the use of names as against mysterious "magic numbers",
since the programmer would KNOW that after
VAT_MULTIPLIER = 1.19
then displaying in a debugger or other interactive session that
PARTICULAR instance of the value 1.19 would show the name string
'VAT_MULTIPLIER' as well (or no doubt a more structured name constructed
on the fly, identifying package and module-within-package too).

The problem is that a `name' is a mapping from a symbolic identifier to
an object and that this mapping must either be global (with the
attendant name collision issues) or within a context (with the
attendant question of `in which context').
Mandating names for _everything_ would complicate the language by
forcing it to provide builtin names for a lot of elementary building
blocks: so for most types of objects it's best to "gently nudge". For
functions, classes, modules, and packages, I think the naming is
important enough (as explained above) to warrant a syntax including the
name; better, therefore, not to complicate the language by providing
another different syntax in each case just to allow the name to be
omitted -- why encourage a pratice that's best discouraged, at the price
of language simplicity?

I agree. This is why I write
(defun foo (x) ...)

rather than
(setf (symbol-function 'foo) (lambda (x) ...))

However, there are places where names are cumbersome. They are
certainly cumbersome for most literal objects like numbers and strings.
There are circumstances where they are cumbersome for function
objects. The function (lambda (x) (+ x 3)) doesn't really need a name.
It isn't any big deal if you give it a name, but it's a trivial
annoyance to be forced to give it a name. It becomes a major annoyance
when you write code in continuation-passing-style or monadic style.

It is an annoyance when refactoring code, too. Suppose you had written
(mapcar #'sin list-of-numbers)

and you realize that the numbers are in degrees. With an unnamed
function, this is an easy fix:
(mapcar (lambda (x) (sin (deg->rad x))) list-of-numbers)

With a named function, you have to do one of these:
(flet ((sin-d (x) (sin (deg->rad x))))
(mapcar #'sin-d list-of-numbers))

or
(mapcar (flet ((sin-d (x) (sin (deg->rad x)))) #'sin-d)
list-of-numbers)


Matthias Felleisen once suggested that *every* internal function should
be named. I just said `continuations'. He immediately amended his
statement with `except those'.
 
P

Paul Rubin

I think it's reasonable to make a name a part of functions, classes and
modules because they may often be involved in tracebacks (in case of
uncaught errors): to me, it makes sense to let an error-diagnosing
tracebacks display packages, modules, classes and functions/methods
involved in the chain of calls leading to the point of error _by name_.

But it would be even nicer if the traceback could point back to the
exact location in the source code where the function definition
occurred, and that wouldn't need any name for the function.
 
M

Martin Rydstr|m

Didn't want to trigger some flamewar;-), but, yes, if that was my only
choice, I'd much rather use small, simple Scheme than huge, complicated,
rich, powerful Common Lisp. ((But in this case I'm biased by early
experiences, since when I learned and used Lisp-ish languages there WAS
no Common Lisp, while Scheme was already there, although not quite the
same language level as today, I'm sure;-)).

If that was in the early to mid eighties, which I seem to recall you
mentioning, the Lisp dialects mostly in use were huger, more
complicated, richer and more powerful than Common Lisp in many, if not
most, respects, as far as I can tell. Common Lisp is a (later)
augmented least common denominator of those Lisps. The really big
thing that's newer and greater in CL is CLOS and the condition system.

',mr
 
A

Alex Martelli

Joe Marshall said:
The problem is that a `name' is a mapping from a symbolic identifier to
an object and that this mapping must either be global (with the
attendant name collision issues) or within a context (with the
attendant question of `in which context').

Why is that a problem? Even for so-called "global" names, Python
supports a structured, hierarchical namespace, so there can never be any
collision betwen the "globals" of distinct modules (including modules
which happen to have the same name but live in distinct packages or
subpackages) -- I did mention that names could usefully be displayed in
some strcutured form such as apackage.somemodule.thefunction but perhaps
I was too tangential about it;-).

Matthias Felleisen once suggested that *every* internal function should
be named. I just said `continuations'. He immediately amended his
statement with `except those'.

If I used continuations (I assume you mean in the call/cc sense rather
than some in which I'm not familiar?) I might feel the same way, or not,
but I don't (alas), so I can't really argue the point either way for
lack of real-world experience.

But if we can agree to name every function except continuations I'll be
content: Python doesn't support continuations, so it's consistent for it
to require every function to be named (since in its case "every
function" and "every function except continuations" coincide;-).


Alex
 
A

Alex Martelli

Paul Rubin said:
But it would be even nicer if the traceback could point back to the
exact location in the source code where the function definition
occurred, and that wouldn't need any name for the function.

I believe a name is a useful "summary" or "conceptual handle" for a
thing, saving us from having to describe/analyze/recognize it in more
detail each and every time. "Need" may be too strong a word, but I
maintain there's _usefulness_ (and reasonableness, and good common
sense) in the naming.


Alex
 
A

Alex Martelli

Joe Marshall said:
No, it is a question about consistency. Why are functions singled out?

They're not: packages, modules and classes also have names; as I pointed
out, the common factor among these four types is that instance of these
types are liable to occur in tracebacks &c, where a (good) name is
particularly useful as a summary reminder of what the thing is.


Alex
 
P

Petr Prikryl

Joe Marshall wrote:
...

Event the trivial function can have a name. Does it make
the trivial function more understandable if I do not give a name?
I understand what lambda means, but it is clearer for me to see
something like (with more meaningful name than shown here):
... return x + 5

and to use it like...
8

Still, I can anonymize it later, if I really need

or I can give it another name

In my opinion, the cry for lambda in Python comes
from people used to functional languages, because
they are not used to the procedural style and it
does not fit them at the beginning.
Frankly, functional style seems "more strange" to me
(i.e. the counter example). Still, I do not say it is bad.
But it never came to my mind to say "Please, rewrite
the LISP so that it looked more like Pascal. I do not like
the lambda."

Also, Python is compiled (to bytecode), imperative language,
and the form of definition of the chunks of code is more
natural to the compiler and to the customs
how the procedural program is written and how its parts
are referenced and put together to form bigger chunks
of code.
I think it's reasonable to make a name a part of functions, classes and
modules because they may often be involved in tracebacks (in case of
uncaught errors): to me, it makes sense to let an error-diagnosing
tracebacks display packages, modules, classes and functions/methods
involved in the chain of calls leading to the point of error _by name_.

I agree with Alex here. I USUALLY do not need debugger, but sometimes
it is exremely handy to discover what happens (through debugger or few
print commands, its a matter of taste). And Python is very fine here:
['__call__', '__class__', '__delattr__', '__dict__', '__doc__',
'__get__',
'__getattribute__', '__hash__', '__init__', '__module__', '__name__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
'__str__', 'func_closure', 'func_code', 'func_defaults', 'func_dict',
'func_doc', 'func_globals', 'func_name'] 'fn'

and even the anonymized function still knows its original name and type
>>> a[0].__name__ 'fn'
>>> type(a[0])
<type 'function'>

If lambda functions in Python are tweaked internally into normal Python
functions...
['__call__', '__class__', '__delattr__', '__dict__', '__doc__',
'__get__',
'__getattribute__', '__hash__', '__init__', '__module__', '__name__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__',
'__str__', 'func_closure', 'func_code', 'func_defaults', 'func_dict',
'func_doc', 'func_globals', 'func_name'] <type 'function'>

.... what makes them better, than the named function? If I do not loose
anything by simplification, I want to have it simpler.

pepr
 
M

M Jared Finder

Alex said:
That would _complicate_ the language (by adding a rule). I repeat what
I've already stated repeatedly: a good criterion for deciding which good
practices a language should enforce and which ones it should just
facilitate is _language simplicity_. If the enforcement is done by
adding rules or constructs it's probably not worth it; if the
"enforcements" is done by NOT adding extra constructs it's a double win
(keep the language simpler AND push good practices).

Your reasoning, taken to the extreme, implies that an assembly language,
by virtue of having the fewest constructs, is the best designed language
ever. But we know that not to be the case -- rarely do even embedded
developers program in assembly language any more. Why?

Because assembly language is so simple that it's very tedious to write.
There's no function call primitive -- you have to manually generate a
function call pattern yourself. There's no looping primitive -- you
have to manually generate a looping pattern yourself. I feel sorry for
the person who's debugging an assembly program using manually generated
code that implements a vtable-based dynamic dispatch.

Any feature that allows me to write less code to do the same thing has a
huge positive -- the reduction of human generated, and therefore error
prone, code. I think the advantages of anonymous functions:
a) explicitly documenting that the function is used in only one place
b) enabling generic iteration and looping routines
c) not having to maintain a unique name for the function
d) making the language more elegant
e) making the language simpler to implement
greatly outweigh the small disadvantage of adding one additional
construct to the language.

-- MJF
 
P

Petr Prikryl

Alex said:
I think it's reasonable to make a name a part of functions, classes and
modules because they may often be involved in tracebacks (in case of
uncaught errors): to me, it makes sense to let an error-diagnosing
tracebacks display packages, modules, classes and functions/methods
involved in the chain of calls leading to the point of error _by name_.
[...]
E.g. consider the Smalltalk code (assumed to be the body of a method):

aCollection
do: [:each |
each > 0 ifTrue: [^ true]].
^ false.

which iterates over a collection checking to see if any element is > 0. If so
then the method answers true ("^" -- spelled "return" in Java), otherwise it
answers false. In that code,
[^ true]
is syntactically and semantically an anonymous function, which is only invoked
if the antecedent is true (in point of fact the compiler inlines that function
away but I don't think that's relevant here). The passage beginning
[:each | ...
and reaching to the matching ] is also an anonymous function with one parameter
(each) which is applied to each element of the collection in turn. (In this
case it really is an anonymous function, even at the implementation level.)
What "name" would you give to either of them ? I don't believe that /any/ name
is possible, and certainly that no name is desirable.

for element in aCollection:
if element > 0:
return True
return False

And adding "def test(aCollection):" does not make it more complex,
in my opinion. And possibly, the chunk of code may be written
in some other way, more Pythonically. Maybe the attempt to
rewrite programs from other languages into Python and
the effort to use the same "tricks" like in the original program,
causes the cry for preserving lambda in future Python.

pepr
 
M

Michele Simionato

Ken said:
I was not thinking about the thread issue (of which I know little). The
big deal for Cells is the dynamic bit:

(let ((*dependent* me))
(funcall (rule me) me))

Then if a rule forces another cell to recalculate itself, *dependent*
gets rebound and (the fun part) reverts back to the original dependent
as soon as the scope of the let is exited.

Python 2.5 has a "with" statement (yes, the name is Lispish on purpose)
that could be used to implement this. See
http://www.python.org/dev/peps/pep-0343

Michele Simionato
 
A

Antoon Pardon

Op 2006-05-09 said:
Antoon said:
Op 2006-05-09 said:
Antoon Pardon wrote:
Op 2006-05-09, Pisin Bootvong schreef <[email protected]>:
Is this a Slippery Slope fallacious argument?
(http://c2.com/cgi/wiki?SlipperySlope)

No it is not.

[...]

So the question I have is: Why is requiring me to give this function
a name considered a good thing, when it leads to a situation that
is considered bad practice in case of a number.

--

Slippery Slope::
"Argumentation that A is bad, because A might lead to B, and B
to C, and we all know C is very bad."

But I have seen noone here argue that requiring functions to be named
leads to requiring all variables to be named.

If I misinterpret your intended meaning, then I'm terribly sorry.
However let me clarify how I understand your statements -- English is
not my native language but I'm quite sure I have good English skill.

you said:

So in that question, one of the your assumption is that:

"it (required function naming) ***LEADS*** to a situation that is
considered bad practice in case of a number (required number naming)"

The bad practice I was talking about was that the number was essentially
given a meaningless name that didn't add any information. There is
no adavantage using 'one' as a variable name over using 1 directly.

If for some reason I needed a 2 in the code every time 'one' was
used, I can hardy turn

one = 1

into

one = 2

Because people would still see things like:

foo(one)

In the cade and would expect this to be equivallent with

foo(1)

and not the equivallent of

foo(2)
You have not seen anyone here
variables to be named"

But obviously I have seen someone "argue that requiring functions to be
named leads to requiring **NUMBER** to be named"

IMO you misinterpreted someome's word that way.
And you said that requiring number to be named is bad.

Yes that is the starting point. But that is just an
example of what bad practices will follow: the use
of meaningless names.
Where did I misunderstand that you, with all good faith, were not
trying to say that "A leads B which is bad so "why is A considered a
good thing""? And how is it not slippery argument?

This is my argument:

1) Using meaningless names is a bad coding practice. (with the number example)
2) Requiring names in particular circumstances will
lead to meaninless names in those circumstances.
3) Requiring names for functions will lead to
meanless names for some functions.
4) Requiring names for functions will thus lead to
bad coding practice in a number of cases.
To put my response another way, I would ask:
Does Python require you to name all your number? expression? (Hint: the
answer is no)

So where did you get the idea that requiring function to be named, as
Python does, leads to requiring number to be named? Is it in a research
anywhere? Or do majority of people here agree so (that it leads to)?

I didn't get that idea.
Your example:

def incr_cnt_by_one(obj):
obj.cnt += 1

treat_all(lst, incr_cnt_by_one)

Did not in anyway show that the number one is required to be named. 1
in that code is written literally; you didn't have to write "one = 1"
first. The function to increment obj.cnt by one is named. but the
number 1 itself is not named.

You missed the point entirely. Isn't "one" a horrible name to use?
It doesn't buy you anything over the use of 1 and may even cause
confusion should anyone ever write something like one = 2.

And isn't incr_cnt_by_one just as horrible a name to use. If I ever
needed another function I couldn't just use another body with
the same function name because something like

def incr_cnt_by_one(obj):
obj.buzy = False

would probably cause just as much confusion as

one = 2.

But if I am required to give such simple functions a name it
is almost inevitable I have to use such a horrible name.
the alternative is to use bland names like "func" but what
advantage does the name give you anyway? I either call
all such one-liners "func" and then there is debug help
in the name or I will have to be sure that every function
somehow has a unique name. Having to track how far I am
in the range of func1, func2 accross the code seem very
cumbersome and not worth the while. Think of what may
happen if you start using different packages each with
there own func1, func2 range of functions.
 
C

Chris Uppal

Petr said:
for element in aCollection:
if element > 0:
return True
return False

[I'm not sure whether this is supposed to be an example of some specific
language (Python ?) or just a generic illustration. I'll take it as the
latter, since it makes my point easier to express. I'll also exaggerate, just
a little...]

But now, in order to hack around the absence of a sensible and useful
feature -- /only/ in order to do so -- you have added two horrible new
complications to your language. You have introduced a special syntax to
express conditionals, and (worse!) a special syntax to express looping. Not
only does that add a huge burden of complexity to the syntax, and semantics, of
the language (and, to a lesser extent, its implementation), but it also throws
out any semblance of uniformity.

Once you've started down that route then you've lost all hope of user-defined
control structures which operate on a par with the built-in ones. And please
note that "control structures" are not limited to C-style "for" "while" etc.

E.g. in Java there's an unresolved, and irresolvable, tension between whether a
failing operation should return an error condition or throw an exception -- the
problem is that exceptions are (IMO and most other peoples' here) intended for
exceptional conditions, and for many operations "failure" is not exceptional.
One, highly effective, resolution to the problem is for the operation to be
parameterised with the action to take if it fails (defaulting to code to raise
an exception). In Java that approach, though technically possible, is totally
infeasible due to the pathetic overuse of unwanted syntax, and the underuse of
simple and powerful primitives.

E.g. can you add three-way comparisons (less-than, same-as, greater-than to,
say, Python with corresponding three-way conditional control structures to
supplement "if" etc ? Are they on a semantic and syntactic par with the
existing ones ? In Smalltalk that is trivial (too trivial to be particularly
interesting, even), and I presume the same must be true of Lisp (though I
suspect you might be forced to use macros).

I should say that if your example /is/ in fact Python, then I believe that
language allows fairly deep hooks into the execution mechanism, so that at
least the "for" bit can be mediated by the collection itself -- which is better
than nothing, but nowhere near what I would call "good".

-- chris
 
K

Ketil Malde

But if we can agree to name every function except continuations I'll be
content

FWIW, I disagree:

A simple example, doubling each entry in a list:

map (*2) xs
vs. let double x = x*2 in map double xs

Here's another example, extracting all lines that contain at least one
word:

filter (not.null) . map words . lines

Note that I'm using the following anonymous functions:

not . null
filter (not . null)
map words
filter (not.null) . map words

Would it really improve anything if I named these? It seems
incredibly pedestrian, along the lines of requiring a comments for
every source line:

x++; /* increase x by one */
a[x] = ' '; /* insert a space in a at position x */

Sometimes the best documentation is the code itself. Sometimes the
best name for a function is the code itself.

-k
 
S

sross

I do wonder what would happen to Cells if I ever want to support
multiple threads. Or in a parallel processing environment.

AFAIK It should be fine.
In LW, SBCL and ACL all bindings of dynamic variables are thread-local.

Cheers,
Sean.
 
R

Rob Warnock

+---------------
| Sometimes the best documentation is the code itself.
| Sometimes the best name for a function is the code itself.
+---------------

And there's no reason that an anonymous LAMBDA [even if compiled]
couldn't store its source code in the "name" slot for debugging
printouts [e.g., stack backtraces].


-Rob
 
B

Boris Borcic

Ken said:
"Now if you are like most
people, you think that means X. It does not."

As far as natural language and understanding are concerned, "to mean" means
conformity to what most people understand, Humpty Dumpties notwithstanding.

Cheers.
 

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,785
Messages
2,569,624
Members
45,319
Latest member
LorenFlann

Latest Threads

Top