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

B

brian

Bill said:
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
 
I

I V

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
 
B

Bill Atkins

Serge Orlov said:
Bill said:
Serge Orlov said:
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.
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.


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 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 ;)


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

Not really, because this is not Cells.
 
F

Frank Buss

Alex said:
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 :)
 
B

Bill Atkins

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

Dr.Ruud

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"?
 
C

Chris Uppal

Bill said:
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
 
C

Carl Friedrich Bolz

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:
.... 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)
....212.0

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

Cheers,

Carl Friedrich Bolz
 
P

Patrick May

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
 
B

brian

Bill said:
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.
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.
 
B

brian

Patrick said:
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
 
B

Bill Atkins

Chris Uppal said:
Bill said:
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.
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/practical-parsing-binary-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.
 
B

Bill Atkins

Bill said:
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.
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.
 
B

Bill Atkins

Bill Atkins said:
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.
 
T

Thomas F. Burdick

Ken Tilton said:
As for:


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

Ken Tilton

Serge said:
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! said:
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.
 
K

Ken Tilton

Serge said:
Bill said:
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.
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.
 
K

Ken Tilton

Thomas said:
As for:



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

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

Ken Tilton

Ken said:
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
 
K

Ken Tilton

Ken said:
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.




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 :eek:ne)
(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 :eek:bserving-one new-value old-value old-value-bound-p)))

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

(defobserver b (self new-value old-value old-value-bound-p)
(print (list :eek:bserving-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
:eek:ne (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.
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,775
Messages
2,569,601
Members
45,182
Latest member
BettinaPol

Latest Threads

Top