473,326 Members | 2,732 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,326 software developers and data experts.

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

Python, Lambda, and Guido van Rossum

Xah Lee, 2006-05-05

In this post, i'd like to deconstruct one of Guido's recent blog about
lambda in Python.

In Guido's blog written in 2006-02-10 at
http://www.artima.com/weblogs/viewpo...?thread=147358

is first of all, the title “Language Design Is Not Just Solving
Puzzles”. In the outset, and in between the lines, we are told that
“I'm the supreme intellect, and I created Python”.

This seems impressive, except that the tech geekers due to their
ignorance of sociology as well as lack of analytic abilities of the
mathematician, do not know that creating a language is a act that
requires little qualifications. However, creating a language that is
used by a lot people takes considerable skill, and a big part of that
skill is salesmanship. Guido seems to have done it well and seems to
continue selling it well, where, he can put up a title of belittlement
and get away with it too.

Gaudy title aside, let's look at the content of his say. If you peruse
the 700 words, you'll find that it amounts to that Guido does not like
the suggested lambda fix due to its multi-line nature, and says that he
don't think there could possibly be any proposal he'll like. The
reason? Not much! Zen is bantered about, mathematician's impractical
ways is waved, undefinable qualities are given, human's right brain is
mentioned for support (neuroscience!), Rube Goldberg contrivance
phraseology is thrown, and coolness of Google Inc is reminded for the
tech geekers (in juxtaposition of a big notice that Guido works
there.).

If you are serious, doesn't this writing sounds bigger than its
content? Look at the gorgeous ending: “This is also the reason why
Python will never have continuations, and even why I'm uninterested in
optimizing tail recursion. But that's for another installment.”. This
benevolent geeker is gonna give us another INSTALLMENT!

There is a computer language leader by the name of Larry Wall, who said
that “The three chief virtues of a programmer are: Laziness,
Impatience and Hubris” among quite a lot of other ingenious
outpourings. It seems to me, the more i learn about Python and its
leader, the more similarities i see.

So Guido, i understand that selling oneself is a inherent and necessary
part of being a human animal. But i think the lesser beings should be
educated enough to know that fact. So that when minions follow a
leader, they have a clear understanding of why and what.

----

Regarding the lambda in Python situation... conceivably you are right
that Python lambda is perhaps at best left as it is crippled, or even
eliminated. However, this is what i want: I want Python literatures,
and also in Wikipedia, to cease and desist stating that Python supports
functional programing. (this is not necessarily a bad publicity) And, I
want the Perl literatures to cease and desist saying they support OOP.
But that's for another installment.

----
This post is archived at:
http://xahlee.org/UnixResource_dir/w...bda_guido.html

* * Xah
* * xa*@xahlee.org
http://xahlee.org/

May 6 '06
267 10447


Alex Martelli wrote:
Ken Tilton <ke*******@gmail.com> wrote:
...
True but circular, because my very point is that () was a great design
choice in that it made macros possible and they made CL almost
infinitely extensible, while indentation-sensitivity was a mistaken
design choice because it makes for very clean code (I agree
wholeheartedly) but placed a ceiling on its expressiveness.

Having to give functions a name places no "ceiling on expressiveness",
any more than, say, having to give _macros_ a name.
As for:

At a syntax-sugar
level, for example, Lisp's choice to use parentheses as delimiter means
it's undesirable, even unfeasible, to use the single character '(' as an
ordinary identifier in a future release of the language.


(defun |(| (aside) (format nil "Parenthetically speaking...~a." aside))
=> |(|
(|(| "your Lisp /is/ rusty.")
=> "Parenthetically speaking...your Lisp /is/ rusty.."

:) No, seriously, is that all you can come up with?

Interestingly, the SECOND lisper to prove himself unable to read the
very text he's quoting. Reread carefully, *USE THE ***SINGLE***
CHARACTER* ... *AS AN ORDINARY IDENTIFIER*. What makes you read a
``PART OF'' that I had never written? You've shown how to use the
characters as *PART* of an identifier [[and I believe it couldn't be the
very start]], and you appear to believe that this somehow refutes my
assertion?


The function name here:

(|(| "Boy, your Lisp is rusty")
-> Boy, your Lisp is rusty.

....is exactly one (1) character long.

(length (symbol-name'|(|) -> 1

Why? (symbol-name '|(|) -> "(" (No, the "s are not part of the name!)

If you want to argue about that, I will have to bring up the Lisp
readtable. Or did you forget that, too?

:)

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.
May 7 '06 #51
al*****@yahoo.com (Alex Martelli) writes:
Bill Atkins <NO**********@rpi.edu> wrote:
...
>
> Read again what I wrote: I very specifically said "ordinary
> *single-character* identifier" (as opposed to "one of many characters
> inside a multi-character identifier"). Why do you think I said
> otherwise, when you just quoted what I had written? (Even just a
> _leading_ ( at the start of an identifier may be problematic -- and just
> as trivial as having to give names to functions, of course, see below).
Well, the same technique can obviously be used for:

(let ((|(| 3)))
(+ |(| 4)))
;; => 7

The length of the identifier is irrelevant...


But it cannot be a SINGLE CHARACTER, *just* the openparenthesis.

Wow, it's incredible to me that you STILL can't read, parse and
understand what I have so clearly expressed and repeated!


Read my other post. It's incredible that you STILL haven't considered
the possibility that you're just wrong.
Common Lisp does not support unnamed macros (how would these be
useful?), but nothing stops me from adding these. What use case do
you envision for anonymous macros?
None, just like there is none for anonymous functions -- there is
nothing useful I can do with anonymous functions that I cannot do with
named ones.


Sure there are.

Does Python have any support for closures? If so, ignore this point.
But if not, what about examples like this:

(defun make-window (window observer)
;; initialization code here
(add-handler window 'close
(lambda (event)
(notify observer event)))
;; more code)

Being able to keep pass around state with functions is useful.

There are also cases where a function is so trivial that the simplest
way to describe it is with its source code, where giving it a name and
putting it at the beginning of a function is just distracting and
time-consuming. E.g.:

(remove-if (lambda (name)
(find #\- name :test #'char=))
list-of-names)

What's the sense of giving that function its own name? It's much
clearer to simply write it in place. Yes, it's _possible_ to use
named functions, but in this case its functionality is so simple that
it's clearer to simply type it in place. Why is this expressiveness a
bad thing, aside from its power to wreck an indentation-significant
language?
> Anyway, I repeat: *any* design choice (in a language, or for that matter
> any other artefact) has consequences. As Paul Graham quotes and
> supports his unnamed friend as saying, Python lets you easily write code
> that *looks* good, and, as Graham argues, that's an important issue --
> and, please note, a crucial consequence of using significant
> indentation. Alien whitespace eating nanoviruses are no more of a worry
> than alien parentheses eating nanoviruses, after all.
It *is* an important issue, but it's also a subjective issue. I find
Lisp to be far prettier than any syntax-based language, so it's far
from an objective truth that Python code often looks good - or even at
all.


The undeniable truth, the objective fact, is that *to most programmers*
(including ones deeply enamored of Lisp, such as Graham, Tilton, Norvig,
...) Python code looks good; the Lisp code that looks good to YOU (and,
no doubt them), and palatable to me (I have spoken of "eerie beauty"),
just doesn't to most prospective readers. If you program on your own,
or just with a few people who share your tastes, then only your taste
matters; if you want to operate in the real world, maybe, as I've
already pointed out, to build up a successful firm faster than had ever
previously happened, this *DOESN'T SCALE*. Essentially the same issue
I'm explaining on the parallel subthread with Tilton, except that he
fully agrees with my aesthetic sense (quoting Tilton, "No argument. The
little Python I wrote while porting Cells to Python was strikingly
attractive") so this facet of the jewel needed no further belaboring
there.


And I'm sure Kelly Clarkson sounds better *to most listeners* but that
doesn't mean she's a better musician than Hendrix. The fact that most
people are used to ALGOL-like languages does not mean that ALGOL-like
languages are more aesthetically pleasing on their own merits.

Plus, I can easily write code that looks good without using a language
that enforces indentation rules. Lisp's regular syntax lets Emacs do
it for me with a simple C-M-a C-M-q. What could be easier?


If you need to edit and reformat other people's code with Emacs to find
it "looks good", you've made my point: code exists to be read, far more
than it's written, and Python's design choice to keep punctuation scarce
and unobtrusive obviates the need to edit and reformat code that way.


That's not what I'm saying at all. My point is that I can write a
function, counting on Emacs to keep it indented for me, and then after
making a series of changes to it, a mere C-M-a C-M-q takes them into
account and bam, no-fuss indenting.

Alex


--
This is a song that took me ten years to live and two years to write.
- Bob Dylan
May 7 '06 #52
Bill Atkins <NO**********@rpi.edu> writes:
Does Python have any support for closures? If so, ignore this point.
But if not, what about examples like this:

(defun make-window (window observer)
;; initialization code here
(add-handler window 'close
(lambda (event)
(notify observer event)))
;; more code)
Python has closures but you can only read the closed variables, not
write them.
Being able to keep pass around state with functions is useful.

There are also cases where a function is so trivial that the simplest
way to describe it is with its source code, where giving it a name and
putting it at the beginning of a function is just distracting and
time-consuming. E.g.:

(remove-if (lambda (name)
(find #\- name :test #'char=))
list-of-names)


If I read that correctly, in Python you could use

filter(list_of_names, lambda name: '-' not in name)
or
[name for name in list_of_names if '-' not in name]

Both of these have become more natural for me than the Lisp version.
May 7 '06 #53
I V
On Sat, 06 May 2006 21:19:58 -0400, Bill Atkins wrote:
There are also cases where a function is so trivial that the simplest
way to describe it is with its source code, where giving it a name and
putting it at the beginning of a function is just distracting and
time-consuming. E.g.:

(remove-if (lambda (name)
(find #\- name :test #'char=))
list-of-names)

What's the sense of giving that function its own name? It's much
clearer to simply write it in place. Yes, it's _possible_ to use
named functions, but in this case its functionality is so simple that
it's clearer to simply type it in place. Why is this expressiveness a
bad thing, aside from its power to wreck an indentation-significant
language?


Well, you can do that with python's current,
non-indentation-significance-wrecking, lambda syntax, so I don't think
that's a particularly persuasive example. Note also that the only
limitation python has on where you can define functions is that you can't
define them inside expressions (they have to be statements), so you could
define a named function right before the call to remove-if, which removes
some of the advantage you get from the lambda (that is, the function
definition is not very far from when it's used).

Actually, I think the limitation on python that is operative here is not
significant whitespace, but the distinction between statements and
expressions.

(crossposts trimmed)
May 7 '06 #54
Alex Martelli wrote:
Ken Tilton <ke*******@gmail.com> wrote:
...
True but circular, because my very point is that () was a great design
choice in that it made macros possible and they made CL almost
infinitely extensible, while indentation-sensitivity was a mistaken
design choice because it makes for very clean code (I agree
wholeheartedly) but placed a ceiling on its expressiveness.

Having to give functions a name places no "ceiling on expressiveness",
any more than, say, having to give _macros_ a name.


As was pointed out, if a Lisper wants anonymous macros, it's a cinch to
create a scheme to allow this. Whether or not it's a good idea in
general, if someone feels they need it, they can do it. An attitude like
that beats "No you can't do that, but you shouldn't do it anyway."
(defun |(| (aside) (format nil "Parenthetically speaking...~a." aside))
=> |(|
(|(| "your Lisp /is/ rusty.")
=> "Parenthetically speaking...your Lisp /is/ rusty.."

Interestingly, the SECOND lisper to prove himself unable to read the
very text he's quoting. Reread carefully, *USE THE ***SINGLE***
CHARACTER* ... *AS AN ORDINARY IDENTIFIER*. What makes you read a
``PART OF'' that I had never written? You've shown how to use the
characters as *PART* of an identifier [[and I believe it couldn't be the
very start]], and you appear to believe that this somehow refutes my
assertion?
Those vertical bars are just quoting characters, rather like the quoting
characters you'd undoubtedly need in Python to create a variable name
with a space in it.
The point is, OF COURSE any design choice places limitations on future
design choices; but some limitations are even DESIRABLE (a language
where *every* single isolated character could mean anything whatsoever
would not be "expressive", but rather totally unreadable) or at least
utterly trivial (syntax-sugar level issues most typically are).
Yeah, a language like that would probably have some strange name like
"TeX." But we'd never know, because nobody would ever use it.

Yes, we are, because the debate about why it's better for Python (as a
language used in real-world production systems, *SCALABLE* to extremely
large-scale ones) to *NOT* be insanely extensible and mutable is a
separate one -- Python's uniformity of style allows SCALABILITY of
teams, and teams-of-teams, which is as crucial in the real world as
obviously not understood by you (the law you misquoted was about adding
personnel to a LATE project making it later -- nothing to do with how
desirable it can be to add personnel to a large and growing collection
of projects, scaling and growing in an agile, iterative way to meet
equally growing needs and market opportunities).


You seem to be using the most primitive definition of scalable that
there is, viz, if one coder can write one one page program in a day, two
coders can write two (or more likely 1.8) one page programs in a day.
Lispers tend to the view that *OF COURSE* most other decent languages
scale linearly, but what we want is force multipliers and exponential
scaling, not "throw money at it" scaling. Thus program writing programs,
program analyzing programs, compiled domain specific languages and great
quotes like

"I'd rather write programs to write programs than write programs."

"Programs that write programs are the happiest programs in the world."
May 7 '06 #55
al*****@yahoo.com (Alex Martelli) writes:
Yes, we are, because the debate about why it's better for Python (as a
language used in real-world production systems, *SCALABLE* to extremely
large-scale ones) to *NOT* be insanely extensible and mutable is a
separate one -- Python's uniformity of style allows SCALABILITY of
teams, and teams-of-teams, which is as crucial in the real world as
obviously not understood by you (the law you misquoted was about adding
personnel to a LATE project making it later -- nothing to do with how
desirable it can be to add personnel to a large and growing collection
of projects, scaling and growing in an agile, iterative way to meet
equally growing needs and market opportunities).

This specific debate grew from your misuse of "scalable" to mean or
imply "a bazillion feechurz can [[and, implicitly, should]] be added to
a language, and therefore anything that stands in the way of feechuritis
is somehow holding the language back". That's bad enough, even though
in its contextual misuse of "scalable" it breaks new ground, and I don't
want to waste even more time re-treading *old* ground as to whether the
"*insane* extensibility" afforded by macros is a good or a bad thing in
a language to be used for real-world software production (as opposed to
prototyping and research).


It's interesting how much people who don't have macros like to put
them down and treat them as some arcane art that are too "*insane*"ly
powerful to be used well.

They're actually very straightforward and can often (shock of shocks!)
make your code more readable, without your efficiency taking a hit.

For example, at work I recently had to generate PDF reports in PHP.
Certain sections would need to be indented, and then once they were
done, I wanted to move back out to the previous level of indentation.
I ended up with stuff like this (totally made up on the spot, but
conveys the general idea):

function out_main_text( ) {
$old_indent = $pdf->indentation;
$pdf->indent_to( $pdf->indentation + 4 );

out_header();
out_facts();

$pdf->set_indentation( $old_indent );
}

function out_header() {
$old_indent = $pdf->indentation;
$pdf->indent_to( $pdf->indentation + 4 );

$pdf->write( "some text" );

$pdf->set_indentation( $old_indent );
}

function out_facts() {
$old_indent = $pdf->indentation;
$pdf->indent_to( $pdf->indentation + 4 );

out_some_subsection();
out_another_subsection();

$pdf->set_indentation( $old_indent );
}
Obviously, this is very much pseudocode. The point is that managing
indentation was a hassle, because each of these subfunctions indents
to a new position. This can pretty clearly get tedious, and is
definitely error-prone, especially when you consider that different
groups of functions are called depending upon the input and that some
of the functions might return early.

But why should I have to worry about any of this? Why can't I do:

(with-indentation (pdf (+ (indentation pdf) 4))
(out-header)
(out-facts))

and then within, say out-facts:

(with-indentation (pdf (+ (indentation pdf) 4))
(write pdf "some text"))

More readable, and no bookkeeping to worry about. This is great! And
here's the macro:

(defmacro with-indentation (pdf new-value &body body)
(let ((old-indent (gensym)))
`(let ((,old-indent (indentation pdf)))
(unwind-protect (progn ,@body)
(setf (indentation pdf) ,old-indent)))))

Bam, all of that bookkeeping, all of those potential errors have taken
care of themselves. WITH-INDENTATION will expand into code that uses
UNWIND-PROTECT to ensure that the indentation always gets returned to
its previous value, even if an exception occurs or the code within
calls RETURN. The WITH-INDENTATION call sets up an environment where
there is a new indentation level in effect, and then cleans it up when
it's done. I can nest these to my heart's content.

Obviously, to someone totally unfamiliar with Lisp, the contents of
that macro are pretty daunting. But you're crazy if you argue that
having WITH-INDENTATION around isn't an improvement over manually
ensuring that indentation gets saved and restored for every function
call.

I could even generalize this (as CLISP does) to this:

(letf (((indentation pdf) (+ 4 (indentation pdf))))
(write "some text"))

Now I can use LETF to temporarily set any value at all for as long as
the code inside is running, and to restore it when it's done.

Macros rock.

--
This is a song that took me ten years to live and two years to write.
- Bob Dylan
May 7 '06 #56
I V <wr******@gmail.com> writes:
On Sat, 06 May 2006 21:19:58 -0400, Bill Atkins wrote:
There are also cases where a function is so trivial that the simplest
way to describe it is with its source code, where giving it a name and
putting it at the beginning of a function is just distracting and
time-consuming. E.g.:

(remove-if (lambda (name)
(find #\- name :test #'char=))
list-of-names)

What's the sense of giving that function its own name? It's much
clearer to simply write it in place. Yes, it's _possible_ to use
named functions, but in this case its functionality is so simple that
it's clearer to simply type it in place. Why is this expressiveness a
bad thing, aside from its power to wreck an indentation-significant
language?


Well, you can do that with python's current,
non-indentation-significance-wrecking, lambda syntax, so I don't think
that's a particularly persuasive example. Note also that the only
limitation python has on where you can define functions is that you can't
define them inside expressions (they have to be statements), so you could
define a named function right before the call to remove-if, which removes
some of the advantage you get from the lambda (that is, the function
definition is not very far from when it's used).

Actually, I think the limitation on python that is operative here is not
significant whitespace, but the distinction between statements and
expressions.

(crossposts trimmed)


You're right, I was replying to Alex's assertion that "there is
nothing useful I can do with anonymous functions that I cannot do with
named ones."

--
This is a song that took me ten years to live and two years to write.
- Bob Dylan
May 7 '06 #57
Bill Atkins <NO**********@rpi.edu> wrote:
...
Does Python have any support for closures? If so, ignore this point.
Ignored, since closures are there.
Being able to keep pass around state with functions is useful.
Sure, but naming the functions doesn't hamper that.
There are also cases where a function is so trivial that the simplest
way to describe it is with its source code, where giving it a name and
putting it at the beginning of a function is just distracting and
time-consuming. E.g.:

(remove-if (lambda (name)
(find #\- name :test #'char=))
list-of-names)

What's the sense of giving that function its own name? It's much
clearer to simply write it in place. Yes, it's _possible_ to use
named functions, but in this case its functionality is so simple that
it's clearer to simply type it in place. Why is this expressiveness a
bad thing, aside from its power to wreck an indentation-significant
language?
Offering several "equivalently obvious" approaches to performing just
one same task is bad for many reasons: you end up with a bigger language
(with all the attending ills) with no extra power, and you diminish the
style uniformity of programs written in the language, hampering the
ability of many people to work in the same codebase with no "ownership".

Like most all design principles, "there should be one, and preferably
only one, obvious way to perform a task" is an _ideal_, unattainable in
its perfect form in this sublunar world; but I find it an extremely
helpful guideline to design, and it does pervade Python (many of the
planned improvements for the future Python 3.0, which will be allowed to
be backwards-incompatible with 2.*, involve *removing* old features
made, in this sense, redundant by enhancements in recent versions; my
only beef here is that _not enough_ is scheduled for removal, alas).

people are used to ALGOL-like languages does not mean that ALGOL-like
languages are more aesthetically pleasing on their own merits.


"on their own merits" can't be the case -- as Wittgenstein pointed out,
we ARE talking about the natural history of human beings. A frequently
observed aestethic preference for X over Y says nothing about X and Y
``on their own merits'' (if such noumena could even be envisaged), but
it's empirically important about X and Y _when many human beings must
interact with them_.

Being "used to" some kinds of languages has nothing to do with it: when
I met Python I was most "used to" perl (and cognizant of Forth, scheme,
pre-common Lisp, sh, APL, ...), nevertheless Python immediately struck
me as allowing particularly beautiful coding. And I specifically named
many Lisp experts as acknowledging exacty the same thing, further
demolishing your "used to" strawman...
Alex
May 7 '06 #58
Bill Atkins <NO**********@rpi.edu> wrote:
...
Believe it or not, _you_ got it wrong.
Acknowledged: Common Lisp is even MORE insane (note that the quote
"INSANELY extensible" is from Tilton) than I believed -- I'm pretty sure
that the Lisp dialects I used in 1979-1981 didn't go to such crazy
extremes, and neither did Scheme.
Buh? The project doesn't have to be late for Brooks's law to hold;
adding programmers, so goes Brooks reasoning, will always increase the
time required to complete the project because of various communication
issues.
And here is where we check if you're as gracious about admitting your
errors, as I am about mine. Brooks' law is:

"""Adding manpower to a late software project makes it later."""

These are Brooks' words, literally. OK so far?

Your claim, that adding programmers will ALWAYS increase the time, is
not just wrong, but utterly ridiculous. I can't put it better than the
wikipedia:
"""
Misconceptions

A commonly understood implication of Brooks' law is that it will be more
productive to employ a smaller number of very talented (and highly paid)
programmers on a project than to employ a larger number of less talented
programmers, since individual programmer productivity can vary greatly
between highly talented and efficient programmers and less talented
programmers. However, Brooks' law does not mean that starving a project
of resources by employing fewer programmers beyond a certain point will
get it done faster.
"""

Moreover, check out the research on Pair Programming: it scientifically,
empirically confirms that "two heads are better than one", which should
surprise nobody. Does this mean that there aren't "various
communication issues"? Of course there are, but there's no implied
weighting of these factors wrt the _advantages_ of having that second
person on the team (check the Pair Programming literature for long lists
of those advantages).

Only empirical research can tell us where the boundary is -- when
productivity is decreased by going from N to N+1. A lot depends on
imponderable issues such as personality meshes or clashes, leadership
abilities at hands, methodology used, etc. All of this is pretty
obvious, making your assertion that Brooks held otherwise actionable (as
libel, by Brooks) in many legislations.

As it happens, I also have examples in which adding (a few carefully
selected) people to a software project that WAS (slightly) late put that
project back on track and made it deliver successfully on schedule.
Definitely not the "pointy-haired boss" management style of "throwing
warm bodies at the problem" that Brooks was fighting against, but,
consider: the project's velocity has suffered because [a] the tech lead
has his personal (usually phenomenal) productivity hampered by a painful
condition requiring elective surgery to abate, and [b] nobody on the
team is really super-experienced in the intricacies of cryptography, yes
some very subtle cryptographic work turns out to be necessary. One day,
the tech lead calls in -- the pain has gotten just too bad, he's going
for surgery, and will be out of the fray for at least one week.

I still remember with admiration how my Director reacted to the
emergency: he suspended two other projects, deciding that, if THEIR
deadlines were broken, that would be a lesser damage to the company than
if this one slipped any further; and cherrypicked exactly two people --
one incredibly flexible "jack of all trades" was tasked with getting up
to speed on the project and becoming the acting TL for it, and an
excellent cryptography specialist was tasked to dig deep into the
project's cryptography needs and solve them pronto.

So, We Broke Brooks' Law -- the cryptographer did his magic, and
meanwhile the JOAT ramped up instantly and took the lead (kudos to the
Jack's skills, to the clarity and transparency of the previous TL's
work, to the agile methodologies employed throughout, AND to the
uniformity of style of one language which will stay unnamed here)... and
the project delivered on time and within budget. We had one extra
person (two "replacements" for one TL's absence), yet it didn't make the
late software project even later -- it brought it back on track
perfectly well.

I have many other experiences where _I_ was that JOAT (and slightly
fewer ones where I was the specialist -- broadly speaking, I'm more of a
generalist, but, as needs drive, sometimes I do of necessity become the
specialist in some obscure yet necessary technology... must have
happened a dozen times over the 30 years of my careers, counting
graduate school...).

This set of experiences in no way tarnishes the value of Brooks' Law,
but it does *put it into perspective*: done JUST RIGHT, by sufficiently
brilliant management, adding A FEW people with exactly the right mix of
skills and personality to a late software project CAN save the bacon,

Fair enough. But what does Python offer above any garbage-collected
language that makes it so scalable?


Uniformity of style, facilitating egoless programming; a strong cultural
bias for simplicity and clarity, and against cleverness and obscurity;
"just the right constraints" (well, mostly;-), such as immutability at
more or less the right spots (FP languages, with _everything_ immutable,
have an edge there IN THEORY... but in practice, using them most
effectively requires a rare mindset/skillset, making it hard to "scale
up" teams due to the extra difficulty of finding the right people).
Alex
May 7 '06 #59
Paul Rubin <http://ph****@NOSPAM.invalid> wrote:
...
Yes, we are, because the debate about why it's better for Python (as a
language used in real-world production systems, *SCALABLE* to extremely
large-scale ones) to *NOT* be insanely extensible and mutable is a
separate one -- Python's uniformity of style allows SCALABILITY of
teams, and teams-of-teams, which is as crucial in the real world ...
My current take on Lisp vs Python is pretty close to Peter Norvig's
(http://www.norvig.com/python-lisp.html):

Python has the philosophy of making sensible compromises that make
the easy things very easy, and don't preclude too many hard
things. In my opinion it does a very good job. The easy things are
easy, the harder things are progressively harder, and you tend not
to notice the inconsistencies. Lisp has the philosophy of making
fewer compromises: of providing a very powerful and totally
consistent core. This can make Lisp harder to learn because you
operate at a higher level of abstraction right from the start and
because you need to understand what you're doing, rather than just
relying on what feels or looks nice. But it also means that in
Lisp it is easier to add levels of abstraction and complexity;
Lisp makes the very hard things not too hard.


Sure -- however, Python makes them not all that hard either. Peter and
I have uncannily similar tastes -- just last month we happened to meet
at the same Arizona Pueblo-ancestral-people ruins-cum-museum (we both
independently chose to take vacation during spring break week to take
the kids along, we both love the Grand Canyon region, we both love
dwelling on ancient cultures, etc -- so I guess the coincidence wasn't
TOO crazy -- but it still WAS a shock to see Peter walk into the museum
at the same time I was walking out of it!-). Yes, it IS easier to add
complexity in Lisp; that's a good summary of why I prefer Python.

I've heard many times that your current employer uses Python for all
kinds of internal tools; I hadn't heard that it was used in Very Large
projects over there. I'd be interested to hear how that's been
working out, since the biggest Python projects I'd heard of before
(e.g. Zope) are, as you say, toy-sized throwaways compared to the
stuff done regularly over there at G.


Sorry, but I'm not authorized to speak for my employer nor to reveal
details of our internal systems -- hey, I cannot even say how many
servers we have; the "corporate approved metrics" is ``several
thousands''...;-). The most that managed to get approved for external
communication you'll find in the "Python at Google" presentation that
Chris di Bona and Greg Stein hold at times in various venues, and that's
not saying much -- good thing I'm not the one giving those
presentations, as I might find hard to stick to the approved line;-)
Alex
May 7 '06 #60
Ken Tilton <ke*******@gmail.com> wrote:
...
Why? (symbol-name '|(|) -> "(" (No, the "s are not part of the name!)

If you want to argue about that, I will have to bring up the Lisp
readtable. Or did you forget that, too?


Mea culpa -- it wasn't in the Lisp(s) I used 25+ years ago, nor in
Scheme; I've never used Common Lisp in anger, and obviously "just
dabbling" gives one no good feel for how *INSANELY COMPLICATED* (or, if
you prefer, "insanely extensible") it truly is.

I personally think these gyrations are a good example of why Python, the
language that "fits your brain" and has simplicity among its goals, is
vastly superior for production purposes. Nevertheless, I admit I was
wrong!
Alex
May 7 '06 #61
al*****@yahoo.com (Alex Martelli) writes:
Bill Atkins <NO**********@rpi.edu> wrote:
...
Believe it or not, _you_ got it wrong.
Acknowledged: Common Lisp is even MORE insane (note that the quote
"INSANELY extensible" is from Tilton) than I believed -- I'm pretty sure
that the Lisp dialects I used in 1979-1981 didn't go to such crazy
extremes, and neither did Scheme.
Buh? The project doesn't have to be late for Brooks's law to hold;
adding programmers, so goes Brooks reasoning, will always increase the
time required to complete the project because of various communication
issues.


And here is where we check if you're as gracious about admitting your
errors, as I am about mine. Brooks' law is:

"""Adding manpower to a late software project makes it later."""

These are Brooks' words, literally. OK so far?


You are correct.

I posted too hastily. Here is what my paragraph ought to have said:

Buh? The project doesn't have to be late for Brooks's law to hold;
adding programmers *in the middle of a project*, so goes Brooks
reasoning, will always increase the time required to complete the
project because of various communication issues.

Agree?
Your claim, that adding programmers will ALWAYS increase the time, is
not just wrong, but utterly ridiculous. I can't put it better than the
wikipedia:
"""
Misconceptions

A commonly understood implication of Brooks' law is that it will be more
productive to employ a smaller number of very talented (and highly paid)
programmers on a project than to employ a larger number of less talented
programmers, since individual programmer productivity can vary greatly
between highly talented and efficient programmers and less talented
programmers. However, Brooks' law does not mean that starving a project
of resources by employing fewer programmers beyond a certain point will
get it done faster.
"""

Moreover, check out the research on Pair Programming: it scientifically,
empirically confirms that "two heads are better than one", which should
surprise nobody. Does this mean that there aren't "various
communication issues"? Of course there are, but there's no implied
weighting of these factors wrt the _advantages_ of having that second
person on the team (check the Pair Programming literature for long lists
of those advantages).

Only empirical research can tell us where the boundary is -- when
productivity is decreased by going from N to N+1. A lot depends on
imponderable issues such as personality meshes or clashes, leadership
abilities at hands, methodology used, etc. All of this is pretty
obvious, making your assertion that Brooks held otherwise actionable (as
libel, by Brooks) in many legislations.
Hahaha.
As it happens, I also have examples in which adding (a few carefully
selected) people to a software project that WAS (slightly) late put that
project back on track and made it deliver successfully on schedule.
Definitely not the "pointy-haired boss" management style of "throwing
warm bodies at the problem" that Brooks was fighting against, but,
consider: the project's velocity has suffered because [a] the tech lead
has his personal (usually phenomenal) productivity hampered by a painful
condition requiring elective surgery to abate, and [b] nobody on the
team is really super-experienced in the intricacies of cryptography, yes
some very subtle cryptographic work turns out to be necessary. One day,
the tech lead calls in -- the pain has gotten just too bad, he's going
for surgery, and will be out of the fray for at least one week.

I still remember with admiration how my Director reacted to the
emergency: he suspended two other projects, deciding that, if THEIR
deadlines were broken, that would be a lesser damage to the company than
if this one slipped any further; and cherrypicked exactly two people --
one incredibly flexible "jack of all trades" was tasked with getting up
to speed on the project and becoming the acting TL for it, and an
excellent cryptography specialist was tasked to dig deep into the
project's cryptography needs and solve them pronto.

So, We Broke Brooks' Law -- the cryptographer did his magic, and
meanwhile the JOAT ramped up instantly and took the lead (kudos to the
Jack's skills, to the clarity and transparency of the previous TL's
work, to the agile methodologies employed throughout, AND to the
uniformity of style of one language which will stay unnamed here)... and
the project delivered on time and within budget. We had one extra
person (two "replacements" for one TL's absence), yet it didn't make the
late software project even later -- it brought it back on track
perfectly well.

I have many other experiences where _I_ was that JOAT (and slightly
fewer ones where I was the specialist -- broadly speaking, I'm more of a
generalist, but, as needs drive, sometimes I do of necessity become the
specialist in some obscure yet necessary technology... must have
happened a dozen times over the 30 years of my careers, counting
graduate school...).

This set of experiences in no way tarnishes the value of Brooks' Law,
but it does *put it into perspective*: done JUST RIGHT, by sufficiently
brilliant management, adding A FEW people with exactly the right mix of
skills and personality to a late software project CAN save the bacon,

Fair enough. But what does Python offer above any garbage-collected
language that makes it so scalable?


Uniformity of style, facilitating egoless programming; a strong cultural
bias for simplicity and clarity, and against cleverness and obscurity;
"just the right constraints" (well, mostly;-), such as immutability at
more or less the right spots (FP languages, with _everything_ immutable,
have an edge there IN THEORY... but in practice, using them most
effectively requires a rare mindset/skillset, making it hard to "scale
up" teams due to the extra difficulty of finding the right people).
Alex


--
This is a song that took me ten years to live and two years to write.
- Bob Dylan
May 7 '06 #62
"Xah Lee" <xa*@xahlee.org> writes:
In this post, i'd like to deconstruct one of Guido's recent blog about
lambda in Python.


Why couldn't you keep this to comp.lang.python where it would almost
be relevant? Before I pulled down the headers, I thought maybe
something interesting was posted to comp.lang.lisp.

Followups set.

--
http://www.david-steuber.com/
1998 Subaru Impreza Outback Sport
2006 Honda 599 Hornet (CB600F) x 2 Crash & Slider
It's OK. You only broke your leg in three places. Walk it off.
May 7 '06 #63


Alex Martelli wrote:
Ken Tilton <ke*******@gmail.com> wrote:
...
Why? (symbol-name '|(|) -> "(" (No, the "s are not part of the name!)

If you want to argue about that, I will have to bring up the Lisp
readtable. Or did you forget that, too?

Mea culpa -- it wasn't in the Lisp(s) I used 25+ years ago, nor in
Scheme; I've never used Common Lisp in anger, and obviously "just
dabbling" gives one no good feel for how *INSANELY COMPLICATED* (or, if
you prefer, "insanely extensible") it truly is.

I personally think these gyrations...


Please. You are the only one gyrating. I was joking with both |(| and
the fact that its length is one, no matter what our eyes tell us.

And I hope to god you were joking with the objection that a Lisper could
not name a variable "(". You would not say something that daft just to
win a Usenet debate, would you? Omigod...I think you did! I mean, look
at the way you were jumping up and down and shouting and accusing Bill
and me of not understanding English and... well, you are an uber tech
geek, what did i expect? All is forgiven. But...

It is vastly more disappointing that an alleged tech genius would sniff
at the chance to take undeserved credit for PyCells, something probably
better than a similar project on which Adobe (your superiors at
software, right?) has bet the ranch. This is the Grail, dude, Brooks's
long lost Silver Bullet. And you want to pass?????

C'mon, Alex, I just want you as co-mentor for your star quality. Of
course you won't have to do a thing, just identify for me a True Python
Geek and she and I will take it from there.

Here's the link in case you lost it:

http://www.lispnyc.org/wiki.clp?page=PyCells

:)

peace, kenny

ps. flaming aside, PyCells really would be amazingly good for Python.
And so Google. (Now your job is on the line. <g>) k

--
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.
May 7 '06 #64
Alex Martelli wrote:
``An unneeded feature "cannot" be added (elegantly) in future releases
of the language'' is just as trivial and acceptable for the unneded
feature ``allow ( as an ordinary single-character identifier'' as for
the unneded feature ``allow unnamed functions with all the flexibility
of named ones''.


You can't be seriously claiming that these two features are equally
(un)needed. Anonymous functions come from the foundations of computer
science - Lamda Calculus. They are just a natural step on the road to
higher level languages. There are useful programming techniques, like
monadic programming, that are infeasible without anonymous functions.
Anonymous functions really add some power to the language.

On the other hand, what do you get by allowing ( as an indentifier?

Significant whitespace is a good thing, but the way it is designed in
Python it has some costs. Can't you simply acknowledge that?

Best regards
Tomasz
May 7 '06 #65
Bill Atkins <NO**********@rpi.edu> wrote:
...
And here is where we check if you're as gracious about admitting your
errors, as I am about mine. Brooks' law is:

"""Adding manpower to a late software project makes it later."""

These are Brooks' words, literally. OK so far?


You are correct.

I posted too hastily. Here is what my paragraph ought to have said:

Buh? The project doesn't have to be late for Brooks's law to hold;
adding programmers *in the middle of a project*, so goes Brooks
reasoning, will always increase the time required to complete the
project because of various communication issues.

Agree?


"What does one do when an essential software project is behind schedule?
Add manpower, naturally. As Figs 2.1 through 2.4 suggest, this may or
may not help".

How do you translate "may or may not help" into "will always increase
the time required", and manage to impute that translation to "Brooks
reasoning"? It *MAY* help -- Brooks says so very explicitly, and from
the Fig 2.4 he quotes just as explicitly you may infer he has in mind
that the cases in which it may help are those in which the project was
badly understaffed to begin with (a very common case, as the rest of the
early parts of chapter 2 explains quite adequately).

I could further critique Brooks for his ignoring specific personal
factors -- some extremely rare guys are able to get up to speed on an
existing project in less time, and requiring less time from those
already on board, than 99.9% of the human race (the JOAT in my previous
example); and, another crucial issue is that sometimes the key reason a
project is late is due to lack of skill on some crucial _specialty_, in
which case, while adding more "generic" programmers "may or may not
help" (in Brooks' words), adding just the right specialist (such as, the
cryptography expert in my previous example) can help a LOT. But, it
would be uncourteous to critique a seminal work in the field for not
anticipating such detailed, nuanced discussion (besides, in the next
chapter Brooks does mention teams including specialists, although if I
recall correctly he does not touch on the issue of a project which finds
out the need for a specialist _late_, as in this case). I'm still more
interested in critiquing your overgeneralization of Brooks' assertions
and reasoning.
Alex
May 7 '06 #66
Alex Martelli wrote:
Having to give functions a name places no "ceiling on expressiveness",
any more than, say, having to give _macros_ a name.
And what about having to give numbers a name?
Yes, we are, because the debate about why it's better for Python (as a
language used in real-world production systems, *SCALABLE* to extremely
large-scale ones) to *NOT* be insanely extensible and mutable is a
separate one -- Python's uniformity of style allows SCALABILITY of
teams, and teams-of-teams
I think this kind of language scalability is most important for Google,
see below.
if your need SCALE, well then, PYTHON IS SCALABLE, and will remain a
*SIMPLE, CLEAN, LITTLE AND POWERFUL LANGUAGE* (letting nobody do
anything INSANE to it;-) while scaling up to whatever size of project(s)
you need (including systems so large...


But honestly, isn't this scalability a result of the data processing
model you use, which is language independent? My point is that when you
have such a well scaling data processing model, most languages scale
well on the "data" and "load" axes, so you pick your language based on
how well it scales on the "teams" and "features" axes.

You seem to claim that there is something in Python that lets it scale
well on "data" and "load", and is not related to "teams" and "features",
and also not related to Google's data processing model. Can you tell
me what it is?

Best regards
Tomasz
May 7 '06 #67
Ken Tilton wrote:
It is vastly more disappointing that an alleged tech genius would sniff
at the chance to take undeserved credit for PyCells, something probably
better than a similar project on which Adobe (your superiors at
software, right?) has bet the ranch. This is the Grail, dude, Brooks's
long lost Silver Bullet. And you want to pass?????


Looking at the description, Cells looks a lot like one of our internal Python
libraries at Enthought. It's quite useful. Not worldchanging, not a silver
bullet, but useful.

--
Robert Kern

"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco

May 7 '06 #68
Tomasz Zielonka <to*************@gmail.com> wrote:
Alex Martelli wrote:
Having to give functions a name places no "ceiling on expressiveness",
any more than, say, having to give _macros_ a name.
And what about having to give numbers a name?


Excellent style, in most cases; I believe most sensible coding guides
recommend it for most numbers -- cfr
<http://en.wikipedia.org/wiki/Magic_number_(programming)> , section
"magic numbers in code".
Yes, we are, because the debate about why it's better for Python (as a
language used in real-world production systems, *SCALABLE* to extremely
large-scale ones) to *NOT* be insanely extensible and mutable is a
separate one -- Python's uniformity of style allows SCALABILITY of
teams, and teams-of-teams


I think this kind of language scalability is most important for Google,
see below.


It's definitely very important for Google, just as for most organization
doing software development on a large scale.

if your need SCALE, well then, PYTHON IS SCALABLE, and will remain a
*SIMPLE, CLEAN, LITTLE AND POWERFUL LANGUAGE* (letting nobody do
anything INSANE to it;-) while scaling up to whatever size of project(s)
you need (including systems so large...


But honestly, isn't this scalability a result of the data processing
model you use, which is language independent? My point is that when you
have such a well scaling data processing model, most languages scale
well on the "data" and "load" axes, so you pick your language based on
how well it scales on the "teams" and "features" axes.


There's one more axis, the "size of software project" one (in some unit
of measure such as function points). That is partly related to
"language level", but it's not _just_ about that -- e.g., Ruby's
language level is basically the same as Python, but (in my modest
experience) it doesn't scale up quite as well to very large software
projects, as it's more prone to subtle coupling between modules (as it
makes it easy for one module to affect others indirectly, by making
modifications to fundamental globals such as "Object" and other built-in
types).

You seem to claim that there is something in Python that lets it scale
well on "data" and "load", and is not related to "teams" and "features",
and also not related to Google's data processing model. Can you tell
me what it is?


I don't think that (apart from whatever infrastructure Google may have
developed, and partly published in various whitepapers while partly
deciding to not discuss it publically) Python's scaling on "data" and
"load", to use your terminology, should be intrinsically different
(i.e., due to language differences) from that of other languages with
similar characteristics, such as, say, Ruby or Smalltalk.
Alex
May 7 '06 #69
Tomasz Zielonka <to*************@gmail.com> wrote:
...
higher level languages. There are useful programming techniques, like
monadic programming, that are infeasible without anonymous functions.
Anonymous functions really add some power to the language.
Can you give me one example that would be feasible with anonymous
functions, but is made infeasible by the need to give names to
functions? In Python, specifically, extended with whatever fake syntax
you favour for producing unnamed functions?

I cannot conceive of one. Wherever within a statement I could write the
expression
lambda <args>: body
I can *ALWAYS* obtain the identical effect by picking an otherwise
locally unused identifier X, writing the statement
def X(<args>): body
and using, as the expression, identifier X instead of the lambda.

On the other hand, what do you get by allowing ( as an indentifier?
Nothing useful -- the parallel is exact.

Significant whitespace is a good thing, but the way it is designed in
Python it has some costs. Can't you simply acknowledge that?


I would have no problem "acknowledging" problems if I agreed that any
exist, but I do not agree that any exist. Please put your coding where
your mouth is, and show me ONE example that would be feasible in a
Python enriched by unlimited unnamed functions but is not feasible just
because Python requires naming such "unlimited" functions.
Alex
May 7 '06 #70
Alex Martelli wrote:
Tomasz Zielonka <to*************@gmail.com> wrote:
Alex Martelli wrote:
> Having to give functions a name places no "ceiling on expressiveness",
> any more than, say, having to give _macros_ a name.


And what about having to give numbers a name?


Excellent style, in most cases; I believe most sensible coding guides
recommend it for most numbers -- cfr
<http://en.wikipedia.org/wiki/Magic_number_(programming)> , section
"magic numbers in code".


I was a bit unclear. I didn't mean constants (I agree with you on
magic numbers), but results of computations, for example

(x * 2) + (y * 3)

Here (x * 2), (y * 3) and (x * 2) + 3 are anonymous numbers ;-)

Would you like if you were forced to write it this way:

a = x * 2
b = y * 3
c = a * b

?

Thanks for your answers to my questions.

Best regards
Tomasz
May 7 '06 #71
Ken Tilton wrote:
It is vastly more disappointing that an alleged tech genius would sniff
at the chance to take undeserved credit for PyCells, something probably
better than a similar project on which Adobe (your superiors at
software, right?) has bet the ranch. This is the Grail, dude, Brooks's
long lost Silver Bullet. And you want to pass?????

C'mon, Alex, I just want you as co-mentor for your star quality. Of
course you won't have to do a thing, just identify for me a True Python
Geek and she and I will take it from there.

Here's the link in case you lost it:

http://www.lispnyc.org/wiki.clp?page=PyCells

:)

peace, kenny

ps. flaming aside, PyCells really would be amazingly good for Python.
And so Google. (Now your job is on the line. <g>) k


Perhaps I'm missing something but what's the big deal about PyCells?
Here is 22-lines barebones implementation of spreadsheet in Python,
later I create 2 cells "a" and "b", "b" depends on a and evaluate all
the cells. The output is

a = negate(sin(pi/2)+one) = -2.0
b = negate(a)*10 = 20.0

=================== spreadsheet.py ==================
class Spreadsheet(dict):
def __init__(self, **kwd):
self.namespace = kwd
def __getitem__(self, cell_name):
item = self.namespace[cell_name]
if hasattr(item, "formula"):
return item()
return item
def evaluate(self, formula):
return eval(formula, self)
def cell(self, cell_name, formula):
"Create a cell defined by formula"
def evaluate_cell():
return self.evaluate(formula)
evaluate_cell.formula = formula
self.namespace[cell_name] = evaluate_cell
def cells(self):
"Yield all cells of the spreadsheet along with current values
and formulas"
for cell_name, value in self.namespace.items():
if not hasattr(value, "formula"):
continue
yield cell_name, self[cell_name], value.formula

import math
def negate(x):
return -x
sheet1 = Spreadsheet(one=1, sin=math.sin, pi=math.pi, negate=negate)
sheet1.cell("a", "negate(sin(pi/2)+one)")
sheet1.cell("b", "negate(a)*10")
for name, value, formula in sheet1.cells():
print name, "=", formula, "=", value

May 7 '06 #72
"Serge Orlov" <Se*********@gmail.com> writes:
Ken Tilton wrote:
It is vastly more disappointing that an alleged tech genius would sniff
at the chance to take undeserved credit for PyCells, something probably
better than a similar project on which Adobe (your superiors at
software, right?) has bet the ranch. This is the Grail, dude, Brooks's
long lost Silver Bullet. And you want to pass?????

C'mon, Alex, I just want you as co-mentor for your star quality. Of
course you won't have to do a thing, just identify for me a True Python
Geek and she and I will take it from there.

Here's the link in case you lost it:

http://www.lispnyc.org/wiki.clp?page=PyCells

:)

peace, kenny

ps. flaming aside, PyCells really would be amazingly good for Python.
And so Google. (Now your job is on the line. <g>) k


Perhaps I'm missing something but what's the big deal about PyCells?
Here is 22-lines barebones implementation of spreadsheet in Python,
later I create 2 cells "a" and "b", "b" depends on a and evaluate all
the cells. The output is

a = negate(sin(pi/2)+one) = -2.0
b = negate(a)*10 = 20.0

=================== spreadsheet.py ==================
class Spreadsheet(dict):
def __init__(self, **kwd):
self.namespace = kwd
def __getitem__(self, cell_name):
item = self.namespace[cell_name]
if hasattr(item, "formula"):
return item()
return item
def evaluate(self, formula):
return eval(formula, self)
def cell(self, cell_name, formula):
"Create a cell defined by formula"
def evaluate_cell():
return self.evaluate(formula)
evaluate_cell.formula = formula
self.namespace[cell_name] = evaluate_cell
def cells(self):
"Yield all cells of the spreadsheet along with current values
and formulas"
for cell_name, value in self.namespace.items():
if not hasattr(value, "formula"):
continue
yield cell_name, self[cell_name], value.formula

import math
def negate(x):
return -x
sheet1 = Spreadsheet(one=1, sin=math.sin, pi=math.pi, negate=negate)
sheet1.cell("a", "negate(sin(pi/2)+one)")
sheet1.cell("b", "negate(a)*10")
for name, value, formula in sheet1.cells():
print name, "=", formula, "=", value


I hope Ken doesn't mind me answering for him, but Cells is not a
spreadsheet (where did you get that idea?). It does apply the basic
idea of a spreadsheet to software - that is, instead of updating value
when some event occurs, you specify in advance how that value can be
computed and then you stop worrying about keeping it updated.

Incidentally, is this supposed to be an example of Python's supposed
"aesthetic pleasantness"? I find it a little hideous, even giving you
the benefit of the doubt and pretending there are newlines between
each function. There's nothing like a word wrapped in pairs of
underscores to totally ruin an aesthetic experience.

P.S. Is this really a spreadsheet? It looks like it's a flat
hashtable...

--
This is a song that took me ten years to live and two years to write.
- Bob Dylan
May 7 '06 #73
Bill Atkins <NO**********@rpi.edu> writes:
Incidentally, is this supposed to be an example of Python's supposed
"aesthetic pleasantness"? I find it a little hideous, even giving you
the benefit of the doubt and pretending there are newlines between
each function. There's nothing like a word wrapped in pairs of
underscores to totally ruin an aesthetic experience.


I don't mean to suggest that your code in particular is hideous -
sorry if it came off that way. It's just that Python code seems a
disappointingly un-pretty after all the discussion about its beauty in
this thread.

--
This is a song that took me ten years to live and two years to write.
- Bob Dylan
May 7 '06 #74
Tomasz Zielonka wrote:
On the other hand, what do you get by allowing ( as an indentifier?

Significant whitespace is a good thing, but the way it is designed in
Python it has some costs. Can't you simply acknowledge that?


One can admit this but what is it worth or how should those costs be
evaluated? This problem is unsolvable because we cannot agree on a
common standard of the value of PL design. All we can do is making the
decisions more visible. Obviously Lispers want the boundary between
application-level and interpreter-level programming as low as possible.
It is still present but making it invisible and expressing language
semantics through the language is regarded of high value. This makes
application level meta-programming as simple as it could be. Others
tend to separate the concerns/boundaries more strictly and standardize
the language while implementing all features regarded necessary for
application-level programming in a huge opaque runtime and a vast
amount of libraries. Application level metaprogramming is restricted to
runtime-reflection, metaclass-protocols, annotations etc. From this
point of view a programming language is basically an interface to a
virtual machine that is much like an operating system i.e. it supports
basic functions and hides complexity. This is the way Java and Python
have gone. The benefit of the latter approach lies not so much in
creating new language capabilities but considering the language as just
another application where requirement engineering and carefull design
can be done without sprawling in every possible direction ( see Forth
as an example for this tendency ). This way standard libraries (
"batteries included" ) become almost equal important and
"language-designer" is a job that is not once be done. This is roughly
my interpretation of GvRs "design view" on PLs that Xah Lee obviously
doesn't get - Xah is not important here, because he never gets anything
right but here are enough Lispers with quite some working brain cells
who seem to think that the best thing to do is giving a programmer
unlimited programming power.

Concluding remark: I'm not sure I want to defend one point of view all
the way long. Although not being a Lisper myself I can clearly see that
the DSL topic is hot these days and the pendulum oscillates into the
direction of more liberty. I have mixed feelings about this but I have
my own strong opinion of how those *can* fit into the CPython design
space and a working model that is to be published soon. As with the
dialectic double negation there is no return to a former position.

May 7 '06 #75
Bill Atkins wrote:
Does Python have any support for closures? If so, ignore this point.
But if not, what about examples like this:

(defun make-window (window observer)
;; initialization code here
(add-handler window 'close
(lambda (event)
(notify observer event)))
;; more code)

Being able to keep pass around state with functions is useful.
I agree and Python supports this. What is interesting is how
counter-intuitive many programmers find this. For example, one of my
colleges (who is a reasonably average programmer), was completely
stumped by this:
def right_partial(fn, *args): .... def inner(*innerargs):
.... return fn(*(innerargs + args))
.... return inner
.... square = right_partial(pow, 2)
square(5) 25 cube = right_partial(pow, 3)
cube(5)

125

(for those of you unfamiliar with Python, the '>>>' and '...' means
that I typed this in the interactive shell)

So I try to use this sort of pattern sparingly because many programmers
don't think of closures as a way of saving state. That might be because
it is not possible to do so in most mainstream languages.

There are already some people in the Python community who think that
Python has already gone too far in supporting "complex" language
features and now imposes two steep a learning curve i.e. you now have
to know a lot to be considered a Python expert. And there is a lot of
resistance to adding features that will raise the bar even higher.
There are also cases where a function is so trivial that the simplest
way to describe it is with its source code, where giving it a name and
putting it at the beginning of a function is just distracting and
time-consuming. E.g.:

(remove-if (lambda (name)
(find #\- name :test #'char=))
list-of-names)
There are some ways to write it in Python (if I understand the code):

# prefered
[name for name in list_of_names if '-' not in name]

OR:

# 2nd choice
def contains_no_dash(name):
return '-' not in name

filter(contains_no_dash, list_of_names)

# not recommended
filter(lambda name: return '-' not in name, list_of_names)

What's the sense of giving that function its own name? It's much
clearer to simply write it in place. Yes, it's _possible_ to use
named functions, but in this case its functionality is so simple that
it's clearer to simply type it in place. Why is this expressiveness a
bad thing, aside from its power to wreck an indentation-significant
language?


There are a few people in the Python community who think that unnamed
functions are inherently inferior to named ones because the name
provides documentation. The majority probably just don't care about the
issue at all. I will say that I am not sure that I know what your LISP
code does. However, even if you didn't understand the Python code in my
named-function version, the name "contains_no_dash" might provide you
with a small clue.

Cheers,
Brian

May 7 '06 #76
Bill Atkins wrote:
"Serge Orlov" <Se*********@gmail.com> writes:
Ken Tilton wrote:
It is vastly more disappointing that an alleged tech genius would sniff
at the chance to take undeserved credit for PyCells, something probably
better than a similar project on which Adobe (your superiors at
software, right?) has bet the ranch. This is the Grail, dude, Brooks's
long lost Silver Bullet. And you want to pass?????

C'mon, Alex, I just want you as co-mentor for your star quality. Of
course you won't have to do a thing, just identify for me a True Python
Geek and she and I will take it from there.

Here's the link in case you lost it:

http://www.lispnyc.org/wiki.clp?page=PyCells

:)

peace, kenny

ps. flaming aside, PyCells really would be amazingly good for Python.
And so Google. (Now your job is on the line. <g>) k
Perhaps I'm missing something but what's the big deal about PyCells?
Here is 22-lines barebones implementation of spreadsheet in Python,
later I create 2 cells "a" and "b", "b" depends on a and evaluate all
the cells. The output is

a = negate(sin(pi/2)+one) = -2.0
b = negate(a)*10 = 20.0

=================== spreadsheet.py ==================
class Spreadsheet(dict):
def __init__(self, **kwd):
self.namespace = kwd
def __getitem__(self, cell_name):
item = self.namespace[cell_name]
if hasattr(item, "formula"):
return item()
return item
def evaluate(self, formula):
return eval(formula, self)
def cell(self, cell_name, formula):
"Create a cell defined by formula"
def evaluate_cell():
return self.evaluate(formula)
evaluate_cell.formula = formula
self.namespace[cell_name] = evaluate_cell
def cells(self):
"Yield all cells of the spreadsheet along with current values
and formulas"
for cell_name, value in self.namespace.items():
if not hasattr(value, "formula"):
continue
yield cell_name, self[cell_name], value.formula

import math
def negate(x):
return -x
sheet1 = Spreadsheet(one=1, sin=math.sin, pi=math.pi, negate=negate)
sheet1.cell("a", "negate(sin(pi/2)+one)")
sheet1.cell("b", "negate(a)*10")
for name, value, formula in sheet1.cells():
print name, "=", formula, "=", value


I hope Ken doesn't mind me answering for him, but Cells is not a
spreadsheet (where did you get that idea?).


It's written on the page linked above, second sentence: "Think of the
slots as cells in a spreadsheet, and you've got the right idea". I'm
not claiming that my code is full PyCell implementation.

It does apply the basic
idea of a spreadsheet to software - that is, instead of updating value
when some event occurs, you specify in advance how that value can be
computed and then you stop worrying about keeping it updated.
The result is the same. Of course, I don't track dependances in such a
tiny barebones example. But when you retrieve a cell you will get the
same value as with dependances. Adding dependances is left as an
exercise.

Incidentally, is this supposed to be an example of Python's supposed
"aesthetic pleasantness"?
Nope. This is an example that you don't need macros and
multi-statements. Ken writes: "While the absence of macros and
multi-statement lambda in Python will make coding more cumbersome". I'd
like to see Python code doing the same if the language had macros and
multi-statement lambda. Will it be more simple? More expressive?
I find it a little hideous, even giving you
the benefit of the doubt and pretending there are newlines between
each function. There's nothing like a word wrapped in pairs of
underscores to totally ruin an aesthetic experience.
I don't think anyone who is not a master of a language can judge
readability. You're just distracted by insignificant details, they
don't matter if you code in that language for many years. I'm not going
to tell you how Lisp Cell code looks to me ;)
P.S. Is this really a spreadsheet? It looks like it's a flat
hashtable...


Does it matter if it's flat or 2D?

May 7 '06 #77
Bill Atkins wrote:
Buh? The project doesn't have to be late for Brooks's law to hold;
adding programmers, so goes Brooks reasoning, will always increase the
time required to complete the project because of various communication
issues.
1. This is not what Brooks says. Brooks was talking about late
projects. Please provide a supporting quote if you wish to continue
to claim that "adding programmers will always increase the time
required to complete the project".
2. There has to be a mechanism where an organization can add
developers - even if it is only for new projects. Python advocates
would say that getting developers up to speed on Python is easy
because:

- it fits most programmers brains i.e. it is similar enough to
languages that most programmers have experience with and the
differences are usually perceived to beneficial (exception:
people from a Java/C/C++ background often perceive dynamic
typing as a misfeature and have to struggle with it)
- the language is small and simple
- "magic" is somewhat frowned upon in the Python community i.e.
most code can be taken at face value without needing to learn a
framework, mini-language, etc. (but I think that the Python
community could do better on this point)

I'm sure that smarter people can think of more points.
Fair enough. But what does Python offer above any garbage-collected
language that makes it so scalable?


See above point - you can more easily bring programmers online in your
organization because most programmers find Python easily learnable.
And, as a bonus, it is actually a pretty flexible, powerful language.

Cheers,
Brian

May 7 '06 #78
I V
On Sat, 06 May 2006 23:05:59 -0700, Alex Martelli wrote:
Tomasz Zielonka <to*************@gmail.com> wrote:
...
higher level languages. There are useful programming techniques, like
monadic programming, that are infeasible without anonymous functions.
Anonymous functions really add some power to the language.


Can you give me one example that would be feasible with anonymous
functions, but is made infeasible by the need to give names to
functions? In Python, specifically, extended with whatever fake syntax
you favour for producing unnamed functions?


Monads are one of those parts of functional programming I've never really
got my head around, but as I understand them, they're a way of
transforming what looks like a sequence of imperative programming
statements that operate on a global state into a sequence of function
calls that pass the state between them.

So, what would be a statement in an imperative language is an anonymous
function that gets added to the monad, and then, when the monad is run,
these functions get executed. The point being, that you have a lot of
small functions (one for each statement) which are likely not to be used
anywhere else, so defining them as named functions would be a bit of a
pain in the arse.

Actually, defining them as unnamed functions via lambdas would be annoying
too, although not as annoying as using named functions - what you really
want is macros, so that what looks like a statement can be interpreted is
a piece of code to be executed later.

Here, for instance, is an article about using scheme (and, in particular,
macros), to produce a fairly elegant monad implementation:

http://okmij.org/ftp/Scheme/monad-in-Scheme.html

May 7 '06 #79
"Serge Orlov" <Se*********@gmail.com> writes:
Bill Atkins wrote:
"Serge Orlov" <Se*********@gmail.com> writes:
> Ken Tilton wrote:
>> It is vastly more disappointing that an alleged tech genius would sniff
>> at the chance to take undeserved credit for PyCells, something probably
>> better than a similar project on which Adobe (your superiors at
>> software, right?) has bet the ranch. This is the Grail, dude, Brooks's
>> long lost Silver Bullet. And you want to pass?????
>>
>> C'mon, Alex, I just want you as co-mentor for your star quality. Of
>> course you won't have to do a thing, just identify for me a True Python
>> Geek and she and I will take it from there.
>>
>> Here's the link in case you lost it:
>>
>> http://www.lispnyc.org/wiki.clp?page=PyCells
>>
>> :)
>>
>> peace, kenny
>>
>> ps. flaming aside, PyCells really would be amazingly good for Python.
>> And so Google. (Now your job is on the line. <g>) k
>
> Perhaps I'm missing something but what's the big deal about PyCells?
> Here is 22-lines barebones implementation of spreadsheet in Python,
> later I create 2 cells "a" and "b", "b" depends on a and evaluate all
> the cells. The output is
>
> a = negate(sin(pi/2)+one) = -2.0
> b = negate(a)*10 = 20.0
>
> =================== spreadsheet.py ==================
> class Spreadsheet(dict):
> def __init__(self, **kwd):
> self.namespace = kwd
> def __getitem__(self, cell_name):
> item = self.namespace[cell_name]
> if hasattr(item, "formula"):
> return item()
> return item
> def evaluate(self, formula):
> return eval(formula, self)
> def cell(self, cell_name, formula):
> "Create a cell defined by formula"
> def evaluate_cell():
> return self.evaluate(formula)
> evaluate_cell.formula = formula
> self.namespace[cell_name] = evaluate_cell
> def cells(self):
> "Yield all cells of the spreadsheet along with current values
> and formulas"
> for cell_name, value in self.namespace.items():
> if not hasattr(value, "formula"):
> continue
> yield cell_name, self[cell_name], value.formula
>
> import math
> def negate(x):
> return -x
> sheet1 = Spreadsheet(one=1, sin=math.sin, pi=math.pi, negate=negate)
> sheet1.cell("a", "negate(sin(pi/2)+one)")
> sheet1.cell("b", "negate(a)*10")
> for name, value, formula in sheet1.cells():
> print name, "=", formula, "=", value
>


I hope Ken doesn't mind me answering for him, but Cells is not a
spreadsheet (where did you get that idea?).


It's written on the page linked above, second sentence: "Think of the
slots as cells in a spreadsheet, and you've got the right idea". I'm
not claiming that my code is full PyCell implementation.


Unfortunately, it's *nothing* like a full PyCell implementation. I
explained what Cells is above. It is not merely a spreadsheet - it is
an extension that allows the programmer to specify how the value of
some slot (Lisp lingo for "member variable") can be computed. It
frees the programmer from having to recompute slot values since Cells
can transparently update them. It has to do with extending the object
system, not with merely setting tables in a hash and then retrieving
them.
It does apply the basic
idea of a spreadsheet to software - that is, instead of updating value
when some event occurs, you specify in advance how that value can be
computed and then you stop worrying about keeping it updated.


The result is the same. Of course, I don't track dependances in such a
tiny barebones example. But when you retrieve a cell you will get the
same value as with dependances. Adding dependances is left as an
exercise.

Incidentally, is this supposed to be an example of Python's supposed
"aesthetic pleasantness"?


Nope. This is an example that you don't need macros and
multi-statements. Ken writes: "While the absence of macros and
multi-statement lambda in Python will make coding more cumbersome". I'd
like to see Python code doing the same if the language had macros and
multi-statement lambda. Will it be more simple? More expressive?


FWIW (absolutely nothing, I imagine), here is my take on your
spreadsheet:

http://paste.lisp.org/display/19766

It is longer, for sure, but it does more and I haven't made any
attempt to stick to some minimum number of lines.
I find it a little hideous, even giving you
the benefit of the doubt and pretending there are newlines between
each function. There's nothing like a word wrapped in pairs of
underscores to totally ruin an aesthetic experience.


I don't think anyone who is not a master of a language can judge
readability. You're just distracted by insignificant details, they
don't matter if you code in that language for many years. I'm not going
to tell you how Lisp Cell code looks to me ;)
P.S. Is this really a spreadsheet? It looks like it's a flat
hashtable...


Does it matter if it's flat or 2D?


Not really, because this is not Cells.

--
This is a song that took me ten years to live and two years to write.
- Bob Dylan
May 7 '06 #80
Alex Martelli wrote:
I cannot conceive of one. Wherever within a statement I could write the
expression
lambda <args>: body
I can *ALWAYS* obtain the identical effect by picking an otherwise
locally unused identifier X, writing the statement
def X(<args>): body
and using, as the expression, identifier X instead of the lambda.


This is true, but with lambda it is easier to read:

http://www.frank-buss.de/lisp/functional.html
http://www.frank-buss.de/lisp/texture.html

Would be interesting to see how this would look like in Python or some of
the other languages to which this troll thread was posted :-)

--
Frank Buss, fb@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
May 7 '06 #81
br***@sweetapp.com writes:
Bill Atkins wrote:
Buh? The project doesn't have to be late for Brooks's law to hold;
adding programmers, so goes Brooks reasoning, will always increase the
time required to complete the project because of various communication
issues.
1. This is not what Brooks says. Brooks was talking about late
projects. Please provide a supporting quote if you wish to continue
to claim that "adding programmers will always increase the time
required to complete the project".


The "always" in my claim should not be there, I admit. Brooks didn't
claim that.

I refer you to pages 17 - 18 of The Mythical Man-Month:

Since software construction is inherently a systems effort - an
exercise in complex interrelationships - communication effort is
great...Adding more men then lengthens, not shortens, the schedule.

It is totally absurd to assume that, simply because a project has not
yet passed its deadline, it will somehow become immune to the kinds of
things Brooks is talking about. His thesis is that adding programmers
to an already-in-progress project will cause a delay, because the new
programmers must be brought up to speed. It does not matter if the
project is eight weeks late or has only been active for a month. This
issue still remains:

The two new men, however competent and however quickly trained, will
require training in the task by one of the experienced men. If this
takes a month, 3 man-months will have been devoted to work not in
the original estimate. (p. 24, TMM-M)

Brooks's Law mentions only late projects, but the rest of his
discussion applies to adding programmers in the middle of *any*
project.

Is this really so radical an idea?
2. There has to be a mechanism where an organization can add
developers - even if it is only for new projects. Python advocates
Obviously.
would say that getting developers up to speed on Python is easy
because:

- it fits most programmers brains i.e. it is similar enough to
languages that most programmers have experience with and the
differences are usually perceived to beneficial (exception:
people from a Java/C/C++ background often perceive dynamic
typing as a misfeature and have to struggle with it)
- the language is small and simple
- "magic" is somewhat frowned upon in the Python community i.e.
most code can be taken at face value without needing to learn a
framework, mini-language, etc. (but I think that the Python
community could do better on this point)
These are not things I look for in a programming language.

I'm sure that smarter people can think of more points.
Fair enough. But what does Python offer above any garbage-collected
language that makes it so scalable?


See above point - you can more easily bring programmers online in your
organization because most programmers find Python easily learnable.
And, as a bonus, it is actually a pretty flexible, powerful language.

Cheers,
Brian


--
This is a song that took me ten years to live and two years to write.
- Bob Dylan
May 7 '06 #82
Paul Rubin schreef:
a cryptographic PRNG seeded with good entropy is supposed to be
computationally indistinguishable from physical randomness


Doesn't your "good entropy" include "physical randomness"?

--
Affijn, Ruud

"Gewoon is een tijger."
May 7 '06 #83
Bill Atkins wrote:
But why should I have to worry about any of this? Why can't I do:

(with-indentation (pdf (+ (indentation pdf) 4))
(out-header)
(out-facts))

and then within, say out-facts:

(with-indentation (pdf (+ (indentation pdf) 4))
(write pdf "some text"))

More readable, and no bookkeeping to worry about. This is great! And
here's the macro: .. [...]

Can you explain to a non-Lisper why macros are needed for this ? I'm a
Smalltalker, and Smalltalk has no macros, nor anything like 'em, but the
equivalent of the above in Smalltalk is perfectly feasible, and does not
require a separate layer of semantics (which is how I think of true macros).

aPdf
withAdditionalIndent: 4
do: [ aPdf writeHeader; writeFacts ].

and

aPdf
withAdditionalIndent: 4
do: [ aPdf write: '... some text...' ].

Readers unfamiliar with Smalltalk may not find this any easier to read that
your Lisp code, but I can assure them that to any Smalltalker that code would
be both completely idiomatic and completely transparent. (Although I think a
fair number of Smalltalkers would choose to use a slightly different way of
expressing this -- which I've avoided here only in order to keep things
simple).

Macros rock.


I have yet to be persuaded of this ;-)

-- chris
May 7 '06 #84
Bill Atkins wrote:
[snip]

Unfortunately, it's *nothing* like a full PyCell implementation. I
explained what Cells is above. It is not merely a spreadsheet - it is
an extension that allows the programmer to specify how the value of
some slot (Lisp lingo for "member variable") can be computed. It
frees the programmer from having to recompute slot values since Cells
can transparently update them. It has to do with extending the object
system, not with merely setting tables in a hash and then retrieving
them.


I have not looked at Cells at all, but what you are saying here sounds
amazingly like Python's properties to me. You specify a function that
calculates the value of an attribute (Python lingo for something like a
"member variable"). Something like this:
class Temperature(object): .... def __init__(self, temperature_in_celsius):
.... self.celsius = temperature_in_celsius
.... def _get_fahrenheit(self):
.... return self.celsius * 9.0 / 5.0 + 32
.... fahrenheit = property(_get_fahrenheit)
.... t = Temperature(0)
t.fahrenheit 32.0 t.celsius = -32
t.fahrenheit -25.600000000000001 t = Temperature(100)
t.fahrenheit

212.0

no metaclass hacking necessary. Works also if you want to allow setting
the property.

Cheers,

Carl Friedrich Bolz

May 7 '06 #85
al*****@yahoo.com (Alex Martelli) writes:
In my opinion (and that of several others), the best way for Python to
grow in this regard would be to _lose_ lambda altogether, since named
functions are preferable


Why? I find the ability to create unnamed functions on the fly
to be a significant benefit when coding in Common Lisp.

Regards,

Patrick

------------------------------------------------------------------------
S P Engineering, Inc. | The experts in large scale distributed OO
| systems design and implementation.
pj*@spe.com | (C++, Java, Common Lisp, Jini, CORBA, UML)
May 7 '06 #86
Bill Atkins wrote:
br***@sweetapp.com writes:
Bill Atkins wrote:
Buh? The project doesn't have to be late for Brooks's law to hold;
adding programmers, so goes Brooks reasoning, will always increase the
time required to complete the project because of various communication
issues.

1. This is not what Brooks says. Brooks was talking about late
projects. Please provide a supporting quote if you wish to continue
to claim that "adding programmers will always increase the time
required to complete the project".


The "always" in my claim should not be there, I admit. Brooks didn't
claim that.

I refer you to pages 17 - 18 of The Mythical Man-Month:

Since software construction is inherently a systems effort - an
exercise in complex interrelationships - communication effort is
great...Adding more men then lengthens, not shortens, the schedule.

It is totally absurd to assume that, simply because a project has not
yet passed its deadline, it will somehow become immune to the kinds of
things Brooks is talking about.


Right. But only when a project is late does Brooks say that adding
programmers will always make it later (the claim that you made). In
other cases he says "Add manpower, ..., this may or may not help". That
seems intuitively obvious to me. If the programmers being added require
extensive training [1] by the team to become productive, and their
contribution to the project will be smaller than that amount (e.g. it
is a small or nearly completed project) then their net impact on the
project will be negative. If, OTOH, the new programmers are able to
quickly understand the project/organization/technologies and almost
immediately make useful contributions, then they are likely to have a
net positive impact.
2. There has to be a mechanism where an organization can add
developers - even if it is only for new projects. Python advocates


Obviously.


It's good that you agree. I think that the ability to add new
productive developers to a project/team/organization is at least part
of what Alex means by "scaleability". I'm sure that he will correct me
if I am wrong.
[list of proposed Python advantages snipped]

These are not things I look for in a programming language.


Fair enough. That doesn't mean that these advantages aren't important
to others or, in some situations, objectively important in the survival
of a project/organization.

For example, imagine that Google had used language X instead of Python
to develop their tools (assume they started with 10 expert X
programmers). Expert X programmers are Y percent more productive than
expert Python programmers. Now Google wants to grow aggressively and
needs 100 times more developer productivity (and expects to need even
more productivity in the future). If it is harder to find/hire/create
experts in language X than Python, then Y will have to be large to make
language X a better choice than Python. Also, if non-expert Python
programmers can be more productive than non-expert X programmers, then
Python also has an advantage. Eric Raymond claims that Python has very
high initial productivity and that becoming an expert is fairly easy.

BTW, I'm not saying that Common Lisp fits X in this example.

Cheers,
Brian

[1] I'm considering introducing bugs or misdesigns that have to be
fixed
as part of training for the purposes of this discussion. Also the
time needed to learn to coordinate with the rest of the team.

May 7 '06 #87
Patrick May wrote:
al*****@yahoo.com (Alex Martelli) writes:
In my opinion (and that of several others), the best way for Python to
grow in this regard would be to _lose_ lambda altogether, since named
functions are preferable


Why? I find the ability to create unnamed functions on the fly
to be a significant benefit when coding in Common Lisp.


1. They don't add anything new to the language semantically i.e. you
can always used a named function to accomplish the same task
as an unnamed one.
2. Giving a function a name acts as documentation (and a named
function is more likely to be explicitly documented than an unnamed
one). This argument is pragmatic rather than theoretical.
3. It adds another construction to the language.

Cheers,
Brian

May 7 '06 #88
"Chris Uppal" <ch*********@metagnostic.REMOVE-THIS.org> writes:
Bill Atkins wrote:
But why should I have to worry about any of this? Why can't I do:

(with-indentation (pdf (+ (indentation pdf) 4))
(out-header)
(out-facts))

and then within, say out-facts:

(with-indentation (pdf (+ (indentation pdf) 4))
(write pdf "some text"))

More readable, and no bookkeeping to worry about. This is great! And
here's the macro: . [...]

Can you explain to a non-Lisper why macros are needed for this ? I'm a
Smalltalker, and Smalltalk has no macros, nor anything like 'em, but the
equivalent of the above in Smalltalk is perfectly feasible, and does not
require a separate layer of semantics (which is how I think of true macros).

aPdf
withAdditionalIndent: 4
do: [ aPdf writeHeader; writeFacts ].

and

aPdf
withAdditionalIndent: 4
do: [ aPdf write: '... some text...' ].

Readers unfamiliar with Smalltalk may not find this any easier to read that
your Lisp code, but I can assure them that to any Smalltalker that code would
be both completely idiomatic and completely transparent. (Although I think a
fair number of Smalltalkers would choose to use a slightly different way of
expressing this -- which I've avoided here only in order to keep things
simple).


To be honest, all I know about Smalltalk are the parts of it that were
grafted onto Ruby. But if your example is doing the same as my code,
then I can't say that there's much of an advantage to using a macro
over this.
Macros rock.


I have yet to be persuaded of this ;-)


My favorite macro is ITERATE, which was written at MIT to add a
looping mini-language to CL. There is a macro called LOOP that is
part of Common Lisp, but ITERATE improves upon it in lots of ways.

The cool thing about ITERATE is that it lets you express looping
concepts in a language designed explicitly for such a purpose, e.g.

(iter (for x in '(1 3 3))
(summing x)) => 7

(iter (for x in '(1 -3 2))
(finding x maximizing (abs x))) => -3

(iter (for x in '(a b c 1 d 3 e))
(when (symbolp x)
(collect x))) => (a b c d e)

This is a tiny, tiny chunk of what ITERATE offers and of the various
ways these clauses can be combined. But it should be obvious that
being able to express loops this way is going to make your code much
more concise and more readable.

But it gets even better: since ITERATE is a macro, the code passed to
it is error-checked and analyzed at compile-time, and then converted
to primitive Lisp forms - also at compile-time. What this means is
you get all this extra expressive power at absolutely no runtime cost.

At the risk of causing brains to explode, here is what the third ITER
form expands into:

(LET* ((#:LIST17 NIL)
(X NIL)
(#:RESULT16 NIL)
(#:END-POINTER18 NIL)
(#:TEMP19 NIL))
(BLOCK NIL
(TAGBODY
(SETQ #:LIST17 '(A B C 1 D 3 E))
LOOP-TOP-NIL
(IF (ATOM #:LIST17) (GO LOOP-END-NIL))
(SETQ X (CAR #:LIST17))
(SETQ #:LIST17 (CDR #:LIST17))
(IF (SYMBOLP X)
(PROGN
NIL
(PROGN
(SETQ #:TEMP19 (LIST X))
(SETQ #:END-POINTER18
(IF #:RESULT16
(SETF (CDR #:END-POINTER18) #:TEMP19)
(SETQ #:RESULT16 #:TEMP19)))
#:RESULT16))
NIL)
(GO LOOP-TOP-NIL)
LOOP-END-NIL)
#:RESULT16))

This is obviously not something any sane programmer would sit down and
write just to get an efficient loop. But this code is highly tuned;
for instance, it tracks the end of the list it's building up to save
time. I, as a programmer, get all this expressiveness and efficiency
for free. I think that's awesome.

There are lots more examples of macros. My second favorite macro to
use as an example are Peter Seibel's macros for processing binary files:

http://gigamonkeys.com/book/practica...ary-files.html

He builds a set of macros so that in the next chapter he can define an
ID3 tag reader and writer with:

(define-tagged-binary-class id3v2.3-frame ()
((id (frame-id :length 4))
(size u4)
(flags u2)
(decompressed-size (optional :type 'u4 :if (frame-compressed-p flags)))
(encryption-scheme (optional :type 'u1 :if (frame-encrypted-p flags)))
(grouping-identity (optional :type 'u1 :if (frame-grouped-p flags))))
(:dispatch (find-frame-class id)))

The macro generates code for methods that let you read and write ID3
files in a structured way - and it can of course be used for any other
kind of binary file. The :dispatch parameter at the end will jump to
a different class to parse the rest based on the result of executing
the expression attached to it.

There are lots more, too. Lispers and their environments are often
much more proficient at writing and modifying s-expressions than HTML,
so there are neat HTML generation macros like:

(<:html (<:head (<:title "foo"))
(<:body (<:b :style "font-color: orange" "hello!")))

This example uses Marco Baringer's YACLML package. Again, we get this
expressiveness (no need to use closing tags, the ability to let our
editor indent or transpose tags and so on) for free. At compile-time,
these macros all get parsed down to much, much simpler code:

(PROGN
(WRITE-STRING "<html>
<head>
<title>
foo</title>
</head>
<body>
<b style=\"font-color: orange\">
"
*YACLML-STREAM*)
SOME-VARIABLE
(WRITE-STRING "</b>
</body>
</html>
"
*YACLML-STREAM*))

All of the tags have been streamlined down to a single stream
constant. Again we get more expressiveness but still keep killer
efficiency.

There are still more! _On Lisp_ has a lot of interesting ones, like
an embedded Prolog interpreter and compiler:

(<-- (father billsr billjr))
(?- (father billsr ?))

? = billjr

We have Marco Baringer's ARNESI package, which adds continuations to
Common Lisp through macros, even though Common Lisp does not include
them. The Common Lisp Object System (CLOS), although now part of
Common Lisp, could be (and originally was) built entirely from macros
[1]. That should give a rough idea of their power.

There are macros like DESTRUCTURING-BIND, which is part of Common
Lisp, but could just as easily have been written by you or me. It
handles destructuring of lists. A very simple case:

(destructuring-bind (a b c) '(1 2 3)
(+ a b c)) => 6

I hope this gives you a general idea of the coolness of macros.

[1] Strictly speaking, there are a couple of things that the Lisp
implementation has to take care of, but they are not absolutely
essential features.

--
This is a song that took me ten years to live and two years to write.
- Bob Dylan
May 7 '06 #89
br***@sweetapp.com writes:
Bill Atkins wrote:
br***@sweetapp.com writes:
Bill Atkins wrote:
Buh? The project doesn't have to be late for Brooks's law to hold;
adding programmers, so goes Brooks reasoning, will always increase the
time required to complete the project because of various communication
issues.
1. This is not what Brooks says. Brooks was talking about late
projects. Please provide a supporting quote if you wish to continue
to claim that "adding programmers will always increase the time
required to complete the project".
The "always" in my claim should not be there, I admit. Brooks didn't
claim that.

I refer you to pages 17 - 18 of The Mythical Man-Month:

Since software construction is inherently a systems effort - an
exercise in complex interrelationships - communication effort is
great...Adding more men then lengthens, not shortens, the schedule.

It is totally absurd to assume that, simply because a project has not
yet passed its deadline, it will somehow become immune to the kinds of
things Brooks is talking about.


Right. But only when a project is late does Brooks say that adding
programmers will always make it later (the claim that you made). In
other cases he says "Add manpower, ..., this may or may not help". That
seems intuitively obvious to me. If the programmers being added require
extensive training [1] by the team to become productive, and their
contribution to the project will be smaller than that amount (e.g. it
is a small or nearly completed project) then their net impact on the
project will be negative. If, OTOH, the new programmers are able to
quickly understand the project/organization/technologies and almost
immediately make useful contributions, then they are likely to have a
net positive impact.


There is another essay in TMM-M that discusses the difference between
essential complexity and accidental complexity. You might think
Python is really swell, and I might think Common Lisp is really swell,
but at the heart of it there is still what Brooks calls "essential
complexity" - the difficulty of mapping a complicated real-world
situation into a model a computer can handle. So I think using Python
or Lisp will help get rid of a lot of the accidental complexity that
would arise from using C or C++, but it can only do so much; there is
still a lot of complexity involved even in projects that are written
in very high-level languages. IMHO.
2. There has to be a mechanism where an organization can add
developers - even if it is only for new projects. Python advocates


Obviously.


It's good that you agree. I think that the ability to add new
productive developers to a project/team/organization is at least part
of what Alex means by "scaleability". I'm sure that he will correct me
if I am wrong.
[list of proposed Python advantages snipped]

These are not things I look for in a programming language.


Fair enough. That doesn't mean that these advantages aren't important
to others or, in some situations, objectively important in the survival
of a project/organization.


Sure, agreed.
For example, imagine that Google had used language X instead of Python
to develop their tools (assume they started with 10 expert X
programmers). Expert X programmers are Y percent more productive than
expert Python programmers. Now Google wants to grow aggressively and
needs 100 times more developer productivity (and expects to need even
more productivity in the future). If it is harder to find/hire/create
experts in language X than Python, then Y will have to be large to make
language X a better choice than Python. Also, if non-expert Python
programmers can be more productive than non-expert X programmers, then
Python also has an advantage. Eric Raymond claims that Python has very
high initial productivity and that becoming an expert is fairly easy.

BTW, I'm not saying that Common Lisp fits X in this example.

Cheers,
Brian

[1] I'm considering introducing bugs or misdesigns that have to be
fixed
as part of training for the purposes of this discussion. Also the
time needed to learn to coordinate with the rest of the team.


--
This is a song that took me ten years to live and two years to write.
- Bob Dylan
May 7 '06 #90
Bill Atkins <NO**********@rpi.edu> writes:
There are still more! _On Lisp_ has a lot of interesting ones, like
an embedded Prolog interpreter and compiler:

(<-- (father billsr billjr))
(?- (father billsr ?))

? = billjr


Actually, these might not be implemented as macros.

--
This is a song that took me ten years to live and two years to write.
- Bob Dylan
May 7 '06 #91
Ken Tilton <ke*******@gmail.com> writes:
As for:
At a syntax-sugar
level, for example, Lisp's choice to use parentheses as delimiter means
it's undesirable, even unfeasible, to use the single character '(' as an
ordinary identifier in a future release of the language.
(defun |(| (aside) (format nil "Parenthetically speaking...~a." aside))
=> |(|
(|(| "your Lisp /is/ rusty.")
=> "Parenthetically speaking...your Lisp /is/ rusty.."

:) No, seriously, is that all you can come up with?


Well, you have to quote your (s-as-identifiers. I tried a goofy hack
of a reader macro for ( to make this parse:

(let (( ( 10))) ( ))

[The spaces are just for readability, not necessary]

Alas, short of cps-transforming the whole reader, I can't see a
reasonable way to get that to parse as a single sexp that, when
evaluated, returns 10. What my reader macro gave me was five sexps:

\(, LET, \(, ((10)), NIL

I'll follow up with the Lisp code (on c.l.l only), in case I'm missing
something simple.
OK, I propose a duel. We'll co-mentor this:

http://www.lispnyc.org/wiki.clp?page=PyCells

In the end Python will have a Silver Bullet, and only the syntax will
differ, because Python has a weak lambda, statements do not always
return values, it does not have macros, and I do not know if it has
special variables.


I have no idea what the big problem with a multi-line lambda is in
Python, but I wonder if Cells wouldn't run against the same thing. I
often pass around anonymous formulas that eventually get installed in
a slot. Seems annoying to have to name every formula with a
labels-like mechanism.
May 7 '06 #92


Serge Orlov wrote:
Ken Tilton wrote:
It is vastly more disappointing that an alleged tech genius would sniff
at the chance to take undeserved credit for PyCells, something probably
better than a similar project on which Adobe (your superiors at
software, right?) has bet the ranch. This is the Grail, dude, Brooks's
long lost Silver Bullet. And you want to pass?????

C'mon, Alex, I just want you as co-mentor for your star quality. Of
course you won't have to do a thing, just identify for me a True Python
Geek and she and I will take it from there.

Here's the link in case you lost it:

http://www.lispnyc.org/wiki.clp?page=PyCells

:)

peace, kenny

ps. flaming aside, PyCells really would be amazingly good for Python.
And so Google. (Now your job is on the line. <g>) k

Perhaps I'm missing something...


yes, but do not feel bad, everyone gets confused by the /analogy/ to
spreadsheets into thinking Cells /is/ a spreadsheet. In fact, for a
brief period I swore off the analogy because it was so invariably
misunderstood. Even Graham misunderstood it.

But it is such a great analogy! <sigh>
but what's the big deal about PyCells?
Here is 22-lines barebones implementation of spreadsheet in Python,
later I create 2 cells "a" and "b", "b" depends on a and evaluate all
the cells. The output is

a = negate(sin(pi/2)+one) = -2.0
b = negate(a)*10 = 20.0


Very roughly speaking, that is supposed to be the code, not the output.
So you would start with (just guessing at the Python, it has been years
since I did half a port to Python):

v1 = one
a = determined_by(negate(sin(pi/2)+v1)
b = determined_by(negate(a)*10)
print(a) -> -2.0 ;; this and the next are easy
print(b) -> 20
v1 = two ;; fun part starts here
print(b) -> 40 ;; of course a got updated, too

The other thing we want is (really inventing syntax here):

on_change(a,new,old,old-bound?) print(list(new, old, old-bound?)

Then the print statements Just Happen. ie, It is not as if we are just
hiding computed variables behind syntax and computations get kicked off
when a value is read. Instead, an underlying engine propagates any
assignment throughout the dependency graph before the assignment returns.

My Cells hack does the above, not with global variables, but with slots
(data members?) of instances in the CL object system. I have thought
about doing it with global variables such as a and b above, but never
really seen much of need, maybe because I like OO and can always think
of a class to create of which the value should be just one attribute.

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.
May 7 '06 #93


Serge Orlov wrote:
Bill Atkins wrote:
"Serge Orlov" <Se*********@gmail.com> writes:

Ken Tilton wrote:

It is vastly more disappointing that an alleged tech genius would sniff
at the chance to take undeserved credit for PyCells, something probably
better than a similar project on which Adobe (your superiors at
software, right?) has bet the ranch. This is the Grail, dude, Brooks's
long lost Silver Bullet. And you want to pass?????

C'mon, Alex, I just want you as co-mentor for your star quality. Of
course you won't have to do a thing, just identify for me a True Python
Geek and she and I will take it from there.

Here's the link in case you lost it:

http://www.lispnyc.org/wiki.clp?page=PyCells

:)

peace, kenny

ps. flaming aside, PyCells really would be amazingly good for Python.
And so Google. (Now your job is on the line. <g>) k

Perhaps I'm missing something but what's the big deal about PyCells?
Here is 22-lines barebones implementation of spreadsheet in Python,
later I create 2 cells "a" and "b", "b" depends on a and evaluate all
the cells. The output is

a = negate(sin(pi/2)+one) = -2.0
b = negate(a)*10 = 20.0

=================== spreadsheet.py ==================
class Spreadsheet(dict):
def __init__(self, **kwd):
self.namespace = kwd
def __getitem__(self, cell_name):
item = self.namespace[cell_name]
if hasattr(item, "formula"):
return item()
return item
def evaluate(self, formula):
return eval(formula, self)
def cell(self, cell_name, formula):
"Create a cell defined by formula"
def evaluate_cell():
return self.evaluate(formula)
evaluate_cell.formula = formula
self.namespace[cell_name] = evaluate_cell
def cells(self):
"Yield all cells of the spreadsheet along with current values
and formulas"
for cell_name, value in self.namespace.items():
if not hasattr(value, "formula"):
continue
yield cell_name, self[cell_name], value.formula

import math
def negate(x):
return -x
sheet1 = Spreadsheet(one=1, sin=math.sin, pi=math.pi, negate=negate)
sheet1.cell("a", "negate(sin(pi/2)+one)")
sheet1.cell("b", "negate(a)*10")
for name, value, formula in sheet1.cells():
print name, "=", formula, "=", value

I hope Ken doesn't mind me answering for him, but Cells is not a
spreadsheet (where did you get that idea?).

It's written on the page linked above, second sentence: "Think of the
slots as cells in a spreadsheet, and you've got the right idea". I'm
not claiming that my code is full PyCell implementation.


Well it's the Miller Analogy Test, isn't it? <g> Very easy to leave out
a level of abstraction. Like I said in my other reply, /everyone/ leaves
this level out.

Your spreadsheet does not have slots ruled by functions, it has one slot
for a dictionary where you store names and values/formulas.

Go back to your example and arrange it so a and b are actual slots (data
members? fields?) of the spreadsheet class. You can just stuff numbers in a:

sheet1.a = 42

but b should be somehow associated with a rule when sheet1 is created.
As I said in the other post, also associate an on-change callback with
slots a and b.

When you create sheet1:

sheet1 = Spreadsheet(a=42,b=pow(self.a,2))

You should see the callbacks for a and b fire. Then when you:

sheet1.a = 7

You should see them fire again (and each should see old and new values
as one would hope).

The fun part is when the rule associated with b is just:

self.b = somefunction(self)

and somefunction happens to access slot a and it all still works.

One important feature above is that the rule is associated with the slot
when the instance is created, and different instances can have different
ruls for the same slot. In effect, classes become programmable, hence
more reusable -- without this one is forever subclassing to satisfy
different functional requirements.


It does apply the basic
idea of a spreadsheet to software - that is, instead of updating value
when some event occurs, you specify in advance how that value can be
computed and then you stop worrying about keeping it updated.

The result is the same.


Hopefully I have made clear the different levels. With your example, all
variables exist only in the universe of the Spreadsheet class. So you
have a good start on an /interpreter/ that works like the /extension/
Cells provide to Common Lisp.

Do you see the difference?

Apologies for all the pseudo-Python above, btw.

btw, I am not saying Python cannot do PyCells. I got pretty far on a
port from CL to Python before losing interest. You just take your code
above and move it to the metaclass level and, yes, track dependencies,
etc etc etc.

When that is done we can look at a working example and see how well
Python fared without macros and full-blown lambda.
hth, 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.
May 7 '06 #94


Thomas F. Burdick wrote:
Ken Tilton <ke*******@gmail.com> writes:

As for:

At a syntax-sugar
level, for example, Lisp's choice to use parentheses as delimiter means
it's undesirable, even unfeasible, to use the single character '(' as an
ordinary identifier in a future release of the language.
(defun |(| (aside) (format nil "Parenthetically speaking...~a." aside))
=> |(|
(|(| "your Lisp /is/ rusty.")
=> "Parenthetically speaking...your Lisp /is/ rusty.."

:) No, seriously, is that all you can come up with?

Well, you have to quote your (s-as-identifiers. I tried a goofy hack
of a reader macro for ( to make this parse:

(let (( ( 10))) ( ))

[The spaces are just for readability, not necessary]

Alas, short of cps-transforming the whole reader, I can't see a
reasonable way to get that to parse as a single sexp that, when
evaluated, returns 10. What my reader macro gave me was five sexps:

\(, LET, \(, ((10)), NIL

I'll follow up with the Lisp code (on c.l.l only), in case I'm missing
something simple.


Stop, you are scaring the pythonistas. Even Alex thought I was serious
with (|(| "Hi mom"). Ouch that is hard to type.


OK, I propose a duel. We'll co-mentor this:

http://www.lispnyc.org/wiki.clp?page=PyCells

In the end Python will have a Silver Bullet, and only the syntax will
differ, because Python has a weak lambda, statements do not always
return values, it does not have macros, and I do not know if it has
special variables.

I have no idea what the big problem with a multi-line lambda is in
Python, but I wonder if Cells wouldn't run against the same thing. I
often pass around anonymous formulas that eventually get installed in
a slot. Seems annoying to have to name every formula with a
labels-like mechanism.


(a) Right.

(b) Bloated syntax will hurt in a linear way. the decomposition of
application complexity into so many tractable small rules is a nonlinear
win. (ok, painful coding of X does diminish ones use of X, but Cells
force one to use Cells everywhere or not at all, so it comes with its
own press gang, er, discipline.)

(c) (b) might convince GvR to fix (a)

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.
May 7 '06 #95


Ken Tilton wrote:

a = negate(sin(pi/2)+one) = -2.0
b = negate(a)*10 = 20.0

Very roughly speaking, that is supposed to be the code, not the output.
So you would start with (just guessing at the Python, it has been years
since I did half a port to Python):

v1 = one


Sorry, only later did I realize "one" was going in as a named
spreadsheet Cell. I thought maybe Python was a bit of a super-cobol and
had one as a keyword. <g>

kenny
May 7 '06 #96


Ken Tilton wrote:


Serge Orlov wrote:
Ken Tilton wrote:
It is vastly more disappointing that an alleged tech genius would sniff
at the chance to take undeserved credit for PyCells, something probably
better than a similar project on which Adobe (your superiors at
software, right?) has bet the ranch. This is the Grail, dude, Brooks's
long lost Silver Bullet. And you want to pass?????

C'mon, Alex, I just want you as co-mentor for your star quality. Of
course you won't have to do a thing, just identify for me a True Python
Geek and she and I will take it from there.

Here's the link in case you lost it:

http://www.lispnyc.org/wiki.clp?page=PyCells

:)

peace, kenny

ps. flaming aside, PyCells really would be amazingly good for Python.
And so Google. (Now your job is on the line. <g>) k


Perhaps I'm missing something...

yes, but do not feel bad, everyone gets confused by the /analogy/ to
spreadsheets into thinking Cells /is/ a spreadsheet. In fact, for a
brief period I swore off the analogy because it was so invariably
misunderstood. Even Graham misunderstood it.

But it is such a great analogy! <sigh>
but what's the big deal about PyCells?
Here is 22-lines barebones implementation of spreadsheet in Python,
later I create 2 cells "a" and "b", "b" depends on a and evaluate all
the cells. The output is

a = negate(sin(pi/2)+one) = -2.0
b = negate(a)*10 = 20.0

Very roughly speaking, that is supposed to be the code, not the output.
So you would start with (just guessing at the Python, it has been years
since I did half a port to Python):

v1 = one
a = determined_by(negate(sin(pi/2)+v1)
b = determined_by(negate(a)*10)
print(a) -> -2.0 ;; this and the next are easy
print(b) -> 20
v1 = two ;; fun part starts here
print(b) -> 40 ;; of course a got updated, too

The other thing we want is (really inventing syntax here):

on_change(a,new,old,old-bound?) print(list(new, old, old-bound?)

Then the print statements Just Happen. ie, It is not as if we are just
hiding computed variables behind syntax and computations get kicked off
when a value is read. Instead, an underlying engine propagates any
assignment throughout the dependency graph before the assignment returns.

My Cells hack does the above, not with global variables, but with slots
(data members?) of instances in the CL object system.


here it is:

(in-package :cells)

(defmodel useless () ;; defmodel is CLOS defclass plus Cells wiring
((one :initform nil :accessor one :initarg :one)
(a :initform nil :accessor a :initarg :a)
(b :initform nil :accessor b :initarg :b)))

;; defobserver defines a CLOS method just the right way

(defobserver one (self new-value old-value old-value-bound-p)
(print (list :observing-one new-value old-value old-value-bound-p)))

(defobserver a (self new-value old-value old-value-bound-p)
(print (list :observing-a new-value old-value old-value-bound-p)))

(defobserver b (self new-value old-value old-value-bound-p)
(print (list :observing-b new-value old-value old-value-bound-p)))

;; c-in and c? hide more Cells wiring. The long names are c-input and
c-formula, btw

(progn
(print :first-we-make-a-useless-instance)
(let ((u (make-instance 'useless
:one (c-in 1) ;; we want to change it later, so wrap as
input value
:a (c? (- (+ (sin (/ pi 2)) (one self)))) ;;
negate(sin(pi/2)+one)
:b (c? (* (- (a self) 10)))))) ;; negate(a)*10
(print :now-we-change-one-to-ten)
(setf (one u) 10)))

#| output of the above

:first-we-make-a-useless-instance
(:observing-one 1 nil nil)
(:observing-a -2.0d0 nil nil)
(:observing-b -12.0d0 nil nil)
:now-we-change-one-to-ten
(:observing-one 10 1 t)
(:observing-a -11.0d0 -2.0d0 t)
(:observing-b -21.0d0 -12.0d0 t)

|#

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.
May 7 '06 #97
[trimmed groups]

Ken Tilton <ke*******@gmail.com> writes:
yes, but do not feel bad, everyone gets confused by the /analogy/ to
spreadsheets into thinking Cells /is/ a spreadsheet. In fact, for a brief
period I swore off the analogy because it was so invariably misunderstood.
Even Graham misunderstood it.
Count me in.

But it is such a great analogy! <sigh>
but what's the big deal about PyCells?
Here is 22-lines barebones implementation of spreadsheet in Python,
later I create 2 cells "a" and "b", "b" depends on a and evaluate all
the cells. The output is
a = negate(sin(pi/2)+one) = -2.0
b = negate(a)*10 = 20.0


Very roughly speaking, that is supposed to be the code, not the output. So you
would start with (just guessing at the Python, it has been years since I did
half a port to Python):
v1 = one
a = determined_by(negate(sin(pi/2)+v1)
b = determined_by(negate(a)*10)
print(a) -> -2.0 ;; this and the next are easy
print(b) -> 20
v1 = two ;; fun part starts here
print(b) -> 40 ;; of course a got updated, too


do you mean 30?

I've translated my interpretation of the above to this actual python code:

from math import sin, pi
v1 = cell(lambda: 1)
a = cell(lambda:-(sin(pi/2)+v1.val), dependsOn=[v1])
b = cell(lambda: -a.val*10, dependsOn=[a],
onChange=lambda *args: printChangeBlurp(name='b',*args))
print 'v1 is', v1
print 'a is', a # -2.0 ;; this and the next are easy
print 'b is', b # 20
v1.val = 2 # ;; fun part starts here
print 'v1 now is', v1
print 'b now is', b # 30 ;; of course a got updated, too
I get the following printout:

v1 is 1
a is -2.0
b is [cell 'b' changed from <__main__.unbound object at 0xb4e2472c> to 20.0,
it was not bound]20.0
[cell 'b' changed from 20.0 to 30.0, it was bound ] v1 now is 2
b now is 30.0

Does that seem vaguely right?
The other thing we want is (really inventing syntax here):

on_change(a,new,old,old-bound?) print(list(new, old, old-bound?)
Is the above what you want (you can also dynamically assign onChange later
on, as required or have a list of procedures instead)?

Then the print statements Just Happen. ie, It is not as if we are just hiding
computed variables behind syntax and computations get kicked off when a value
is read. Instead, an underlying engine propagates any assignment throughout
the dependency graph before the assignment returns.
Updating on write rather than recalculating on read does in itself not seem
particularly complicated.
My Cells hack does the above, not with global variables, but with slots (data
members?) of instances in the CL object system. I have thought about doing it
with global variables such as a and b above, but never really seen much of
need, maybe because I like OO and can always think of a class to create of
which the value should be just one attribute.


OK, so in what way does the quick 35 line hack below also completely miss your
point?
# (NB. for lispers: 'is' == EQ; '==' is sort of like EQUAL)

def printChangeBlurp(someCell, oldVal, newVal, bound, name=''):
print '[cell %r changed from %r to %r, it was %s]' % (
name, oldVal, newVal, ['not bound', 'bound '][bound]),

_unbound = type('unbound', (), {})() # just an unique dummy value
def updateDependents(dependents):
seen = {}
for dependent in dependents:
if dependent not in seen:
seen[dependent] = True
dependent.recalculate()
updateDependents(dependent._dependents)
class cell(object):
def __init__(self, formula, dependsOn=(), onChange=None):
self.formula = formula
self.dependencies = dependsOn
self.onChange = onChange
self._val = _unbound
for dependency in self.dependencies:
if self not in dependency._dependents:
dependency._dependents.append(self)
self._dependents = []
def __str__(self):
return str(self.val)
def recalculate(self):
newVal = self.formula()
if self.onChange is not None:
oldVal = self._val
self.onChange(self, oldVal, newVal, oldVal is not _unbound)
self._val = newVal
def getVal(self):
if self._val is _unbound:
self.recalculate()
return self._val
def setVal(self, value):
self._val = value
updateDependents(self._dependents)
val = property(getVal, setVal)

'as
May 7 '06 #98
Alexander Schmolck <a.********@gmail.com> writes:
[trimmed groups]

Ken Tilton <ke*******@gmail.com> writes:
yes, but do not feel bad, everyone gets confused by the /analogy/ to
spreadsheets into thinking Cells /is/ a spreadsheet. In fact, for a brief
period I swore off the analogy because it was so invariably misunderstood.
Even Graham misunderstood it.


Count me in.

But it is such a great analogy! <sigh>
> but what's the big deal about PyCells?
> Here is 22-lines barebones implementation of spreadsheet in Python,
> later I create 2 cells "a" and "b", "b" depends on a and evaluate all
> the cells. The output is
> a = negate(sin(pi/2)+one) = -2.0

> b = negate(a)*10 = 20.0


Very roughly speaking, that is supposed to be the code, not the output. So you
would start with (just guessing at the Python, it has been years since I did
half a port to Python):
v1 = one
a = determined_by(negate(sin(pi/2)+v1)
b = determined_by(negate(a)*10)
print(a) -> -2.0 ;; this and the next are easy
print(b) -> 20
v1 = two ;; fun part starts here
print(b) -> 40 ;; of course a got updated, too


do you mean 30?

I've translated my interpretation of the above to this actual python code:

from math import sin, pi
v1 = cell(lambda: 1)
a = cell(lambda:-(sin(pi/2)+v1.val), dependsOn=[v1])
b = cell(lambda: -a.val*10, dependsOn=[a],
onChange=lambda *args: printChangeBlurp(name='b',*args))
print 'v1 is', v1
print 'a is', a # -2.0 ;; this and the next are easy
print 'b is', b # 20
v1.val = 2 # ;; fun part starts here
print 'v1 now is', v1
print 'b now is', b # 30 ;; of course a got updated, too
I get the following printout:

v1 is 1
a is -2.0
b is [cell 'b' changed from <__main__.unbound object at 0xb4e2472c> to 20.0,
it was not bound]20.0
[cell 'b' changed from 20.0 to 30.0, it was bound ] v1 now is 2
b now is 30.0

Does that seem vaguely right?
The other thing we want is (really inventing syntax here):

on_change(a,new,old,old-bound?) print(list(new, old, old-bound?)


Is the above what you want (you can also dynamically assign onChange later
on, as required or have a list of procedures instead)?

Then the print statements Just Happen. ie, It is not as if we are just hiding
computed variables behind syntax and computations get kicked off when a value
is read. Instead, an underlying engine propagates any assignment throughout
the dependency graph before the assignment returns.


Updating on write rather than recalculating on read does in itself not seem
particularly complicated.
My Cells hack does the above, not with global variables, but with slots (data
members?) of instances in the CL object system. I have thought about doing it
with global variables such as a and b above, but never really seen much of
need, maybe because I like OO and can always think of a class to create of
which the value should be just one attribute.


OK, so in what way does the quick 35 line hack below also completely miss your
point?
# (NB. for lispers: 'is' == EQ; '==' is sort of like EQUAL)

def printChangeBlurp(someCell, oldVal, newVal, bound, name=''):
print '[cell %r changed from %r to %r, it was %s]' % (
name, oldVal, newVal, ['not bound', 'bound '][bound]),

_unbound = type('unbound', (), {})() # just an unique dummy value
def updateDependents(dependents):
seen = {}
for dependent in dependents:
if dependent not in seen:
seen[dependent] = True
dependent.recalculate()
updateDependents(dependent._dependents)
class cell(object):
def __init__(self, formula, dependsOn=(), onChange=None):
self.formula = formula
self.dependencies = dependsOn
self.onChange = onChange
self._val = _unbound
for dependency in self.dependencies:
if self not in dependency._dependents:
dependency._dependents.append(self)
self._dependents = []
def __str__(self):
return str(self.val)
def recalculate(self):
newVal = self.formula()
if self.onChange is not None:
oldVal = self._val
self.onChange(self, oldVal, newVal, oldVal is not _unbound)
self._val = newVal
def getVal(self):
if self._val is _unbound:
self.recalculate()
return self._val
def setVal(self, value):
self._val = value
updateDependents(self._dependents)
val = property(getVal, setVal)

'as


Here's how one of the cells examples might look in corrupted Python
(this is definitely not executable):

class FallingRock:
def __init__(self, pos):
define_slot( 'velocity', lambda: self.accel * self.elapsed )
define_slot( 'pos', lambda: self.accel * (self.elapsed ** 2) / 2,
initial_position = cell_initial_value( 100 ) )
self.accel = -9.8

rock = FallingRock(100)
print rock.accel, rock.velocity, rock.pos
# -9.8, 0, 100

rock.elapsed = 1
print rock.accel, rock.velocity, rock.pos
# -9.8, -9.8, -9.8

rock.elapsed = 8
print rock.accel, rock.velocity, rock.pos
# -9.8, -78.4, -627.2

Make sense? The idea is to declare what a slot's value represents
(with code) and then to stop worrying about keeping different things
synchronized.

Here's another of the examples, also translated into my horrific
rendition of Python (forgive me):

class Menu:
def __init__(self):
define_slot( 'enabled',
lambda: focused_object( self ).__class__ == TextEntry and
focused_object( self ).selection )

Now whenever the enabled slot is accessed, it will be calculated based
on what object has the focus. Again, it frees the programmer from
having to keep these different dependencies updated.

--
This is a song that took me ten years to live and two years to write.
- Bob Dylan
May 7 '06 #99
Bill Atkins <NO**********@rpi.edu> writes:
Alexander Schmolck <a.********@gmail.com> writes:
[trimmed groups]

Ken Tilton <ke*******@gmail.com> writes:
yes, but do not feel bad, everyone gets confused by the /analogy/ to
spreadsheets into thinking Cells /is/ a spreadsheet. In fact, for a brief
period I swore off the analogy because it was so invariably misunderstood.
Even Graham misunderstood it.


Count me in.

But it is such a great analogy! <sigh>

> but what's the big deal about PyCells?
> Here is 22-lines barebones implementation of spreadsheet in Python,
> later I create 2 cells "a" and "b", "b" depends on a and evaluate all
> the cells. The output is
> a = negate(sin(pi/2)+one) = -2.0

> b = negate(a)*10 = 20.0

Very roughly speaking, that is supposed to be the code, not the output. So you
would start with (just guessing at the Python, it has been years since I did
half a port to Python):
v1 = one
a = determined_by(negate(sin(pi/2)+v1)
b = determined_by(negate(a)*10)
print(a) -> -2.0 ;; this and the next are easy
print(b) -> 20
v1 = two ;; fun part starts here
print(b) -> 40 ;; of course a got updated, too


do you mean 30?

I've translated my interpretation of the above to this actual python code:

from math import sin, pi
v1 = cell(lambda: 1)
a = cell(lambda:-(sin(pi/2)+v1.val), dependsOn=[v1])
b = cell(lambda: -a.val*10, dependsOn=[a],
onChange=lambda *args: printChangeBlurp(name='b',*args))
print 'v1 is', v1
print 'a is', a # -2.0 ;; this and the next are easy
print 'b is', b # 20
v1.val = 2 # ;; fun part starts here
print 'v1 now is', v1
print 'b now is', b # 30 ;; of course a got updated, too
I get the following printout:

v1 is 1
a is -2.0
b is [cell 'b' changed from <__main__.unbound object at 0xb4e2472c> to 20.0,
it was not bound]20.0
[cell 'b' changed from 20.0 to 30.0, it was bound ] v1 now is 2
b now is 30.0

Does that seem vaguely right?
The other thing we want is (really inventing syntax here):

on_change(a,new,old,old-bound?) print(list(new, old, old-bound?)


Is the above what you want (you can also dynamically assign onChange later
on, as required or have a list of procedures instead)?

Then the print statements Just Happen. ie, It is not as if we are just hiding
computed variables behind syntax and computations get kicked off when a value
is read. Instead, an underlying engine propagates any assignment throughout
the dependency graph before the assignment returns.


Updating on write rather than recalculating on read does in itself not seem
particularly complicated.
My Cells hack does the above, not with global variables, but with slots (data
members?) of instances in the CL object system. I have thought about doing it
with global variables such as a and b above, but never really seen much of
need, maybe because I like OO and can always think of a class to create of
which the value should be just one attribute.


OK, so in what way does the quick 35 line hack below also completely miss your
point?
# (NB. for lispers: 'is' == EQ; '==' is sort of like EQUAL)

def printChangeBlurp(someCell, oldVal, newVal, bound, name=''):
print '[cell %r changed from %r to %r, it was %s]' % (
name, oldVal, newVal, ['not bound', 'bound '][bound]),

_unbound = type('unbound', (), {})() # just an unique dummy value
def updateDependents(dependents):
seen = {}
for dependent in dependents:
if dependent not in seen:
seen[dependent] = True
dependent.recalculate()
updateDependents(dependent._dependents)
class cell(object):
def __init__(self, formula, dependsOn=(), onChange=None):
self.formula = formula
self.dependencies = dependsOn
self.onChange = onChange
self._val = _unbound
for dependency in self.dependencies:
if self not in dependency._dependents:
dependency._dependents.append(self)
self._dependents = []
def __str__(self):
return str(self.val)
def recalculate(self):
newVal = self.formula()
if self.onChange is not None:
oldVal = self._val
self.onChange(self, oldVal, newVal, oldVal is not _unbound)
self._val = newVal
def getVal(self):
if self._val is _unbound:
self.recalculate()
return self._val
def setVal(self, value):
self._val = value
updateDependents(self._dependents)
val = property(getVal, setVal)

'as


Here's how one of the cells examples might look in corrupted Python
(this is definitely not executable):

class FallingRock:
def __init__(self, pos):
define_slot( 'velocity', lambda: self.accel * self.elapsed )
define_slot( 'pos', lambda: self.accel * (self.elapsed ** 2) / 2,
initial_position = cell_initial_value( 100 ) )
self.accel = -9.8

rock = FallingRock(100)
print rock.accel, rock.velocity, rock.pos
# -9.8, 0, 100

rock.elapsed = 1
print rock.accel, rock.velocity, rock.pos
# -9.8, -9.8, -9.8

rock.elapsed = 8
print rock.accel, rock.velocity, rock.pos
# -9.8, -78.4, -627.2

Make sense? The idea is to declare what a slot's value represents
(with code) and then to stop worrying about keeping different things
synchronized.

Here's another of the examples, also translated into my horrific
rendition of Python (forgive me):

class Menu:
def __init__(self):
define_slot( 'enabled',
lambda: focused_object( self ).__class__ == TextEntry and
focused_object( self ).selection )

Now whenever the enabled slot is accessed, it will be calculated based
on what object has the focus. Again, it frees the programmer from
having to keep these different dependencies updated.

--
This is a song that took me ten years to live and two years to write.
- Bob Dylan


Oh dear, there were a few typos:

class FallingRock:
def __init__(self, pos):
define_slot( 'velocity', lambda: self.accel * self.elapsed )
define_slot( 'pos', lambda: self.accel * (self.elapsed ** 2) / 2,
initial_value = cell_initial_value( 100 ) )
self.accel = -9.8

rock = FallingRock(100)
print rock.accel, rock.velocity, rock.pos
# -9.8, 0, 100

rock.elapsed = 1
print rock.accel, rock.velocity, rock.pos
# -9.8, -9.8, 90.2

rock.elapsed = 8
print rock.accel, rock.velocity, rock.pos
# -9.8, -78.4, -527.2

--
This is a song that took me ten years to live and two years to write.
- Bob Dylan
May 7 '06 #100

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

181
by: Tom Anderson | last post by:
Comrades, During our current discussion of the fate of functional constructs in python, someone brought up Guido's bull on the matter: http://www.artima.com/weblogs/viewpost.jsp?thread=98196 ...
30
by: Mike Meyer | last post by:
I know, lambda bashing (and defending) in the group is one of the most popular ways to avoid writing code. However, while staring at some Oz code, I noticed a feature that would seem to make both...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, youll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
0
by: ArrayDB | last post by:
The error message I've encountered is; ERROR:root:Error generating model response: exception: access violation writing 0x0000000000005140, which seems to be indicative of an access violation...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: Shllpp 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.