Python syntax in Lisp and Scheme

G

Grzegorz =?UTF-8?B?Q2hydXBhxYJh?=

Alex said:
Tut-tut. You are claiming, for example, that I mentioned the lack
of distinction between expressions and statements as "too complex for
Python to support": I assert your claim is demonstrably false, and
that I NEVER said that it would be COMPLEX for Python to support such
a lack. What I *DID* say on the subject, and I quote, was:

Sorry if I inadvertantly distorted your words. What I meant by my admittedly
rhetorical statement wa something like: "these features either introduce
too much complexity, or are messy, or otherwise incompatible with Python's
philosophy and for this reason the language refuses to support them." Not
necessarily too complex to *implement*. I do realize that
no-statements-just-expressions is not a particularly challenging design
issue.
It makes the _learner_'s job simple (the rule he must learn is simple),

That is plausible.
and it makes the _programmer_'s job simple (the rule he must apply to
understand what will happens if he codes in way X is simple)

This makes less sense. The rule may be simple but it also limits the
expressiveness of the language and forces the programmer to work around the
limitations in a contorted and far from "simple" way.

I thought the total inability to nest method definitions (while in Python
you get perfectly normal lexical closures, except that you can't _rebind_
outer-scope names -- hey, in functional programming languages you can't
rebind ANY name, yet nobody every claimed that this means they "don't have
true lexical closures"...!-), and more generally the deep split between
the space of objects and that of methods (a split that's simply not there
in Python), would have been show-stoppers for a Schemer, but it's always
nice to learn otherwise.

I don't really feel quite qualified discuss Ruby's design decisions wrt the
relation between methods, procedures and objects, but I don't think the
split between methods and objects is as deep as you claim:

irb(main):011:0> meth="f-o-o".method:)split)
=> #<Method: String#split>
irb(main):012:0> meth.class
=> Method
irb(main):013:0> meth.kind_of?(Object)
=> true
irb(main):014:0> meth.call('-')
=> ["f", "o", "o"]
irb(main):015:0>

I do tend to think that Ruby would be better off with a more unified
treatment of blocks, procedures and methods, but my understanding of the
issues involved is very incomplete. Perhaps Smalltalk experts would be more
qualified to comment on this.
 
P

Philippe

I'd like to know if it may
be possible to add a powerful macro system to Python, while
keeping its amazing syntax, and if it could be possible to
add Pythonistic syntax to Lisp or Scheme, while keeping all
of the functionality and convenience.

Yes. In fact Daniel Silva and I are working on an implementation of
the Python language for the DrScheme development environment. So you
don't get just a pythonistic syntax for Scheme, you directly get
Python in your Scheme environment. It looks like this:
http://www.ccs.neu.edu/home/matthias/Tmp/spy.jpg

Then Scheme programmers can reuse existing Python code and vice-versa.
Python programmers can also use DrScheme's tools with their code.
Here's an example of using DrScheme's Check Syntax tool on Python
code: http://www.ccs.neu.edu/home/matthias/Tmp/spy2.jpg The tool
analyzes the code, colors terms according to their syntactic category,
and draws arrows that show bindings. Note that no modification to the
Check Syntax tool was necessary to get it to work with Python:
everything was handled automatically by DrScheme's underlying syntax
object system. Not all of DrScheme's tools will automatically work
for Python without modifications like Check Syntax does because some
tools, like the Stepper or the analyzer, need to be aware of some
details about the semantics of the language, but the fact that Check
Syntax and DrScheme's test coverage tool already work with Python and
for free is quite encouraging.

Currently most of the Python language has been implemented and the few
remaining things are well within our reach (except for unicode
support). Daniel (who's the main developer actually - my role is just
to criticize everything he does :)) is now working on the FFI so
Python and Scheme programmers can have access to the C-level Python
modules.

There's still a lot of work that remains to be done so if anyone,
whether Python or Scheme programmer, is interested in helping you're
more than welcome to contact us!

Also, if people are interested in knowing how it all works, Daniel
will have a short presentation of the system at the Scheme Workshop in
Boston next month (the day before the Lightweight Languages 2003 (LL3)
conference).

</shameless plug>

Philippe
 
E

Erann Gat

(e-mail address removed) (Mark Brady) wrote in message
Personally I find Scheme and Common Lisp easier to read but that's
just me, I prefer S-exps ...

I am just barely familiar with Lisp and Scheme. However, I always
find comments like the above interesting. I have seen other people
make this claim also.
However, from an earlier post on comp.lang.python comparing a simple
loop.

Scheme
(define vector-fill!
(lambda (v x)
(let ((n (vector-length v)))
(do ((i 0 (+ i 1)))
((= i n))
(vector-set! v i x)))))

Python
def vector_fill(v, x):
for i in range(len(v)):
v = x

To me the Python code is easier to read, and I can't possibly fathom
how somebody could think the Scheme code is easier to read. It truly
boggles my mind.


In Common Lisp you can write:

(defun vector-fill (v x)
(loop for i from 0 below (length v) do
(setf (aref v i) x)))

or

(defun vector-fill (v x)
(dotimes (i (length v))
(setf (aref v i) x)))

But if you focus on examples like this you really miss the point. Imagine
that you wanted to be able to write this in Python:

def vector_fill(v, x):
for i from 0 to len(v)-1:
v = x

You can't do it because Python doesn't support "for i from ... to ...",
only "for i in ...". What's more, you can't as a user change the language
so that it does support "for i from ... to ...". (That's why the xrange
hack was invented.)

In Lisp you can. If Lisp didn't already have LOOP or DOTIMES as part of
the standard you could add them yourself, and the way you do it is by
writing a macro.

That's what macros are mainly good for, adding features to the langauge in
ways that are absolutely impossible in any other language. S-expression
syntax is the feature that enables users to so this quickly and easily.
I can't see
why a LISP programmer would even want to write a macro.

That's because you are approaching this with a fundamentally flawed
assumption. Macros are mainly not used to make the syntax prettier
(though they can be used for that). They are mainly used to add features
to the language that cannot be added as functions.

For example, imagine you want to be able to traverse a binary tree and do
an operation on all of its leaves. In Lisp you can write a macro that
lets you write:

(doleaves (leaf tree) ...)

You can't do that in Python (or any other langauge).

Here's another example of what you can do with macros in Lisp:

(with-collector collect
(do-file-lines (l some-file-name)
(if (some-property l) (collect l))))

This returns a list of all the lines in a file that have some property.
DO-FILE-LINES and WITH-COLLECTOR are macros, and they can't be implemented
any other way because they take variable names and code as arguments.

E.


----

P.S. Here is the code for WITH-COLLECTOR and DO-FILE-LINES:

(defmacro with-collector (var &body body)
(let ( (resultvar (gensym "RESULT")) )
`(let ( (,resultvar '()) )
(flet ( (,var (item) (push item ,resultvar)) )
,@body)
(nreverse ,resultvar))))

(defmacro do-file-lines ((linevar filename &optional streamvar) &body body)
(let ( (streamvar (or streamvar (gensym "S"))) )
`(with-open-file (,streamvar ,filename)
(do ( (,linevar (read-line ,streamvar nil nil)
(read-line ,streamvar nil nil)) )
( (null ,linevar) )
,@body))))

Here's DOLEAVES:

(defmacro doleaves ((var tree) &body body)
`(walkleaves (lambda (,var) ,@body) ,tree))

:)

(defun walkleaves (fn tree)
(iterate loop1 ( (tree tree) )
(if (atom tree)
(funcall fn tree)
(progn (loop1 (car tree)) (and (cdr tree) (loop1 (cdr tree)))))))

; This is the really cool way to iterate
(defmacro iterate (name args &rest body)
`(labels ((,name ,(mapcar #'car args) ,@body))
(,name ,@(mapcar #'cadr args))))

E.
 
K

Kenny Tilton

Grzegorz said:
I can't remember how *exactly* I came to use scheme (unfortuantely I
don't keep a diary), but trying to reconstruct it looks something like
this:

It would be valuable to have what you wrote next in:

http://alu.cliki.net/The Road to Lisp Survey

Lisp there is defined as "any member of the Lisp family".

Aside: oh, great. Now the survey is going to get thrown off the ALU
Cliki by the Iki Police. um, could you all find something less
productive to focus on? Cutting and pasting thirty pages is /so/ helpful
to the Lisp community. Not!!!

You can be response #78...oops, #79.

Or e-mail me a go-ahead and I'll do the legwork.

kenny
 
S

Sampo Smolander

In comp.lang.scheme Paul Rubin said:
I think you could write the scheme code like this:
(define vector-fill! (v x)

I guess parenthesising like

(define (vector-fill! v x)

would be more schemey.

[Followups set to scheme-group only]
 
S

synthespian

Jeremy H. Brown wrote:
(snip)
According to the "Revised(5) Report on the Algorithmic Language
Scheme", "Scheme is a statically scoped and properly tail-recursive
dialect of the Lisp programming language..." It's certainly not a
dialect of Common Lisp, although it is one of CL's ancestors.

I'm sure if you do some web-groveling, you can find some substantial
comparisons of the two; I personally think they have more in common
than not. Here are a few of the (arguably) notable differences:

Scheme Common Lisp
Philosophy minimalism comprehensiveness

(etc)
Hello --

I would just like to point out that there's more choice out there in
the Lisp family than Scheme - Common Lisp.
In particular, I would like to mention ISLISP, which is an
ISO-standard Lisp. Apparently, the story goes somewhat like this: when
lispers went for an ANSI standard, they left out the Europeans and the
Japanese - which were the other people heavily using Lisp at the time.
Thus, ANSI Common Lisp was made all-American. So the people left out
went for an ISO-standard Lisp.
I don't know why this happened, I suspect (and I might be *very*
wrong) it had something to do with competition way back when Lisp were
aiming higher expectations market-wise (the French being very proud of
Prolog :) ).
I have recently bumped into ISLISP. It is pretty good. It has full
documentation and two usable implementations: a GPL TISL, and a free for
non-commercial use OpenLisp (for now, at least, and I can't say for now
if this will change - for the better).
I don't have time to write a comparison table now, but let me just
say that it mentions in its documentation the purpose of merging the
perceived best features of "the family": "It attempts to bridge the gap
between the various incompatible members of the Lisp family of languages
(most notably Common Lisp, Eulisp, LeLisp, and Scheme) by focusing on
standardizing those areas of widespread agreement." (check the URLs
bellow, this quote from KMP's ISLISP site). However, it's not as big as
Common Lisp (but some people mention that Common Lisp is a large as it
is because it ported functionality that was from the Lisp Machines - but
I might be wrong, what do I know about Lisp Machines - I wish...).
ISLISP has objects, generic functions, defmacro and other good
things. One of its stated aims was industry-use, not academia (that's
from the spec).
The TISL implementation is not so much developed as OpenLisp, but
it's functional and GPLed. OpenLisp is lovely, and it beats the hell out
of Scheme and Common Lisp on the *huge* number of platforms it compiles
on. OpenLisp has compiled on over 60 platforms (yes! 16 to 64 bits!),
and is actively ported today to over 20! So, it's pretty amazing, when
you take into consideration that platform differences are an issue,
particularly with Common Lisp implementations (CLISP being the most
portable), when you need to interact with the OS. So, this is a
non-issue solved on OpenLisp, just as it is solved on Python or Perl. It
approaches Perl or Python in portability (or beats them, I dunno).
OpenLisp's author, unfortunately, isn't much of a "marketing" person...
I have tested it under win32 and NetBSD on Alpha.

BTW, I bumped into OpenLisp because of a Lisp-friendly unix shell
account provider,SDF Public Access Unix Network, a non-profit, that
supports OpenLisp for CGI (also having the usual Python/Perl, etc).
I mention ISLISP here because people are unaware of its existence,
and it's quite a jewel, really.
And let's be honest, who needs Python/Perl/Ruby when you have Lisp? ;-)

Well, that's my 2c, get to know and enjoy ISLISP.

Cheers,


Henry


OpenLisp by Eligis
http://christian.jullien.free.fr/
or http://www.eligis.com

TISL GPL'd ISLISP form Tohoku University (Japan) (under active development)
http://www.ito.ecei.tohoku.ac.jp/TISL/index_j.html

ISLISP - Standards http://anubis.dkuug.dk/JTC1/SC22/WG16/open/standard.html

More ISLISP documentation http://www.islisp.info/, this site maintained
by Kent Pitman

ISLISP in Java http://cube.misto.cz/lisp/

TBK's links on ISLISP
http://tkb.mpl.com/~tkb/links/tkb-links-2407134d0e19cfa20a5ebad3c416bc48.html
 
G

Grzegorz Chrupala

Scheme
(define vector-fill!
(lambda (v x)
(let ((n (vector-length v)))
(do ((i 0 (+ i 1)))
((= i n))
(vector-set! v i x)))))

Python
def vector_fill(v, x):
for i in range(len(v)):
v = x

To me the Python code is easier to read, and I can't possibly fathom
how somebody could think the Scheme code is easier to read. It truly
boggles my mind.


Pick a construct your pet language has specialized support, write an
ugly equivalent in a language that does not specifically support it
and you have proved your pet language to be superior to the other
language. (I myself have never used the "do" macro in Scheme and my
impression is few people do. I prefer "for-each", named "let" or the
CL-like "dotimes" for looping).
The point is if you want you can easily implement something like
"range" in Scheme (as shown by other posters). It would be more
illustrative choose an area where one of the languages is inherently
underpowered. For example I was shocked at how awkward Paul Graham's
"accumulator generator" snippet is in Python:

class foo:
def __init__(self, n):
self.n = n
def __call__(self, i):
self.n += i
return self.n

If a set of macros could be written to improve LISP syntax, then I
think that might be an amazing thing. An interesting question to me
is why hasn't this already been done.

There are libraries that let you write Scheme in Python-like
indentation syntax (<URL:http://cliki.tunes.org/Scheme>, look for
Alternative Syntaxes). However, they are not widely used.

Cheers,
 
S

synthespian

Mark said:
Pythonistas who love functional programming may prefer Scheme to
Common Lisp while Pythonistas who want a standard amazing object
system and loads of built in power in their language may prefer Common
Lisp.
(snip)
Regards,
Mark.

Ps. If anyone spots a mistake in this mail please correct me, it will
have been an honest one and not an attempt to slander your favourite
language and I will be glad to be corrected, in other words there is
no need to flame me :)

I would just say that CLOS (Common Lisp Object System) is not "standard"
in the sense people take OOP to be nowadays, but able to encompass and
go beyond the JAVA, C++, Python, etc, paradigm. This fact was
demonstrated briefly on Paul Graham's ANSI Common LISP book, and
elsewhere, and it's basically a satori.

Henry
 
A

Alex Martelli

Grzegorz Chrupala wrote:
...
class foo:
def __init__(self, n):
self.n = n
def __call__(self, i):
self.n += i
return self.n

some might prefer:

def foo(n):
tot = [n]
def acc(i):
tot[0] += i
return tot[0]
return acc

which is roughly equivalent. It's true that most Pythonistas prefer to
use class instances, rather than closures, in order to group together
some state and some behavior, and the language favours that; and Python
separates expressions and statements quite firmly, so one just can't
increment-and-return in one stroke, nor define-and-return-function ditto.
But I don't see how these issues spell "awkwardness" in this case.


Alex
 
B

Bengt Richter

[email protected] (MetalOne) wrote in message news: said:
Scheme
(define vector-fill!
(lambda (v x)
(let ((n (vector-length v)))
(do ((i 0 (+ i 1)))
((= i n))
(vector-set! v i x)))))

Python
def vector_fill(v, x):
for i in range(len(v)):
v = x

I guess you could also just write

v[:] = [x]*len(v)

instead of calling a function (though it takes more space), e.g.,
>>> v=range(10)
>>> id(v),v (9442064, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> v[:] = [55]*len(v)
>>> id(v),v
(9442064, [55, 55, 55, 55, 55, 55, 55, 55, 55, 55])

using id(v) to show that the same object is mutated.
Pick a construct your pet language has specialized support, write an
ugly equivalent in a language that does not specifically support it
and you have proved your pet language to be superior to the other
language. (I myself have never used the "do" macro in Scheme and my
impression is few people do. I prefer "for-each", named "let" or the
CL-like "dotimes" for looping).
The point is if you want you can easily implement something like
"range" in Scheme (as shown by other posters). It would be more
illustrative choose an area where one of the languages is inherently
underpowered. For example I was shocked at how awkward Paul Graham's
"accumulator generator" snippet is in Python:

class foo:
def __init__(self, n):
self.n = n
def __call__(self, i):
self.n += i
return self.n
Do you like this better?
... box = [n]
... def foo(i): box[0]+=i; return box[0]
... return foo
... (51, 123)
There are libraries that let you write Scheme in Python-like
indentation syntax (<URL:http://cliki.tunes.org/Scheme>, look for
Alternative Syntaxes). However, they are not widely used.

Cheers,

Regards,
Bengt Richter
 
A

Alex Martelli

Erann Gat wrote:
...
But if you focus on examples like this you really miss the point. Imagine
that you wanted to be able to write this in Python:

def vector_fill(v, x):
for i from 0 to len(v)-1:
v = x

You can't do it because Python doesn't support "for i from ... to ...",
only "for i in ...". What's more, you can't as a user change the language
so that it does support "for i from ... to ...". (That's why the xrange
hack was invented.)


Almost right, except that xrange is a hack. Since in Python you cannot
change the language to suit your whims, you USE the language (designed
by a pretty good language designer) -- by coding an iterator that is
suitable to put where the ... are in "for i in ...".
In Lisp you can. If Lisp didn't already have LOOP or DOTIMES as part of
the standard you could add them yourself, and the way you do it is by
writing a macro.

Good summary: if you fancy yourself as a language designer, go for Lisp;
if you prefer to use a language designed by somebody else, without you
_or any of the dozens of people working with you on the same project_
being able to CHANGE the language, go for Python.
That's what macros are mainly good for, adding features to the langauge in
ways that are absolutely impossible in any other language. S-expression
syntax is the feature that enables users to so this quickly and easily.

Doesn't Dylan do a pretty good job of giving essentially the same
semantics (including macros) without S-expression syntax? That was
my impression, but I've never used Dylan in production.
For example, imagine you want to be able to traverse a binary tree and do
an operation on all of its leaves. In Lisp you can write a macro that
lets you write:

(doleaves (leaf tree) ...)

You can't do that in Python (or any other langauge).

Well, in Ruby, or Smalltalk, you would pass your preferred code block
to the call to the doleaves iterator, giving something like:

doleaves(tree) do |leaf|
...
end

while in Python, where iterators are "the other way around" (they
get relevant items out rather than taking a code block in), it would be:

for leaf in doleaves(tree):
...

In either case, it may not be "that" (you are not ALTERING the syntax
of the language, just USING it for the same purpose), but it's sure close.
(In Dylan, I do believe you could ``do that'' -- except the surface
syntax would not be Lisp-ish, of course).

Here's another example of what you can do with macros in Lisp:

(with-collector collect
(do-file-lines (l some-file-name)
(if (some-property l) (collect l))))

This returns a list of all the lines in a file that have some property.
DO-FILE-LINES and WITH-COLLECTOR are macros, and they can't be implemented
any other way because they take variable names and code as arguments.

If you consider than giving e.g. the variable name as an argument to
do-file-lines is the crucial issue here, then it's probably quite true
that this fundamental (?) feature "cannot be implemented any other way";
in Ruby, e.g., the variable name would not be an argument to dofilelines,
it would be a parameter at the start of the block receiving & using it:

dofilelines(somefilename) do |l|
collect l if someproperty? l
end

However, it appears to me that the focus on where variable names are
to be determined may be somewhat misplaced. The key distinction does
seem to be: if you're happy using a language as it was designed (e.g.,
in this example, respecting the language designer's concept that the
names for the control variables of a block must appear within | vertical
bars | at the start of the block -- or, in Python, the reversed concept
that they must appear between the 'for' and 'in' in the "for ... in ...:
statement), macros are not relevant; if you do want to design and use
your own language (including, for example, placing variable names in
new and interesting places) then macros can let you do that, while
other constructs would be insufficiently powerful.

If you dream of there being "preferably only one obvious way to do it",
as Pythonistas do (try "import this" at an interactive Python prompt),
macros are therefore a minus; if you revel in the possibilities of there
being many ways to do it, even ones the language designer had never even
considered (or considered and rejected in disgust:), macros then become
a huge plus.

Therefore, I entirely agree that people who pine for macros should
use them in a language that accomodates them quite well, is designed
for them, cherishes and nurtures and exhalts them, like some language
of the Lisp family (be it Common, ISO, Scheme, ...), or perhaps Dylan
(which often _feels_ as if "of the Lisp family" even though it does
not use S-expressions), rather than trying to shoehorn them willy-nilly
into a language to whose overall philosophy they are SO utterly foreign,
like Python (Ruby, and even more Perl, may be a different matter;
google for "Lingua Latina Perligata" to see what Perl is already able
to do in terms of within-the-language language design and syntax
alteration, even without anything officially deemed to be 'macros'...
it IS, after all, a language CENTERED on "more than one way to do it").


Alex
 
A

Alan Crowe

MetalOne wrote
If a set of macros could be written to improve LISP
syntax, then I think that might be an amazing thing. An
interesting question to me is why hasn't this already been
done.

I think the issue is the grandeur of the Lisp vision. More
ambitious projects require larger code bases. Ambition is
hard to quantify. Nevertheless one must face the issue of
scaling. Does code size go as the cube of ambition, or is it
the fourth power of ambition? Or something else entirely.

Lisp aspires to change the exponent, not the constant
factor. The constant factor is very important. That is why
CL has FILL :) but shrinking the constant factor has been
done (and with C++ undone).

Macros can be used to abbreviate code. One can spot that one
is typing similar code over and over again. One says
"whoops, I'm typing macro expansions". Do you use macros to
tune the syntax, so that you type N/2 characters instead of
N characters, or do you capture the whole concept in macro
and eliminate the repetition altogether?

The point is that there is nowhere very interesting to go
with syntax tuning. It is the bolder goal of changing the
exponent, and thus seriously enlarging the realm of the
possible, that excites.

Alan Crowe
 
D

David Rush

It's be interesting to know where people got the idea of learning
Scheme/LISP from (apart from compulsory university courses)?

Emacs. I've noticed over the years that people don't really get Emacs
religion until they've started hacking elisp. I know that the frustration
of having almost-but-not-quite the behavior I wanted on top of having all
that source code was a powerful incentive for me to learn Lisp. Of course
my apreciation of Emacs only increased as I went...

The thing that sealed it for me was re-programming SCWM's behavior so that
I could use X w/no mouse &cet. That got me hooked on Scheme (I had been
hacking SML at roughly the same time while looking for the foundations of
OOP), which was really just about perfect semantically.

david rush
 
R

Rayiner Hashem

Object system no yes
To be fair, most Scheme implementations come with one, and you can always
download an external one if you want.
Macro system syntax-rules defmacro
Again, depends on the implementation. Gambit offers CL-style macros too.
Performance "worse" "better"
Depends on the implementation. Bigloo did a bit better on the Great Computer
Language Shootout than did CMUCL, though, there were complaints that the
CL-code was sub-par. In the few small benchmarks I've tried on my machine,
Bigloo is pretty competitive with CMUCL.
 
F

Frode Vatvedt Fjeld

Alex Martelli said:
Good summary: if you fancy yourself as a language designer, go for
Lisp; if you prefer to use a language designed by somebody else,
without you _or any of the dozens of people working with you on the
same project_ being able to CHANGE the language, go for Python.

I believe it is very unfortunate to view lisp macros as something that
is used to "change the language". Macros allow syntactic abstraction
the same way functions allow functional abstraction, and is almost as
important a part of the programmer's toolchest. While macros _can_ be
used to change the language in the sense of writing your own
general-purpose iteration construct or conditional operator, I believe
this is an abuse of macros, precisely because of the implications this
has for the readability of the code and for the language's user
community.
 
G

Grzegorz Chrupala

Do you like this better?
... box = [n]
... def foo(i): box[0]+=i; return box[0]
... return foo
...

It's still a hack that shows an area where Python has unnecessary
limitations, isn't it?
As Paul Graham says ( said:
Python users might legitimately ask why they can't just write

def foo(n):
return lambda i: return n += i

or even

def foo(n):
lambda i: n += i

Cheers,
-- Grzegorz
 
D

David Rush

... Lispers posting here have gone to pains to state that Scheme is
not a dialect of Lisp but a separate Lisp-like language. Could you
give a short listing of the current main differences (S vs. CL)?

Do you even begin to appreciate how inflammatory such a request is when
posted to to both c.l.l and c.l.s?

Anyway, as a fairly heavily biased Schemer:

Scheme vs Common Lisp

1 name space vs multiple name spaces
This is a bigger issue than it seems on the surface, BTW

#f vs nil
In Scheme an empty list is not considered to be the same
thing as boolean false

emphasis on all values being first-class vs ad-hoc values
Scheme tries to achieve this, Lisp is by conscious design a
compromise system design, for both good and bad

small semantic footprint vs large semantic footprint
Scheme seems relatively easier to keep in mind as an
additional language.CL appears to have several sub-languages
embedded in it. This cuts both ways, mind you.

Thos eare the most obvious surface issues. My main point is that it is
pretty much silly to consider any of the above in isolation. Both languages
make a lot of sense in their design context. I vastly prefer Scheme because
it suits my needs (small semantic footprint, powerful toolkit) far better
than CL (everything is there if you have the time to look for it). I should
point out that I build a lot of funny data structures (suffix trees and
other
IR magic) for which pre-built libraries are both exceedingly rare and
incorrectly optimized for the specific application.

I also like the fact that Scheme hews rather a lot closer to the
theoretical
foundations of CS than CL, but then again that's all part of the small
semantic
footprint for me.

david rush
 
P

prunesquallor

Frode Vatvedt Fjeld said:
I believe it is very unfortunate to view lisp macros as something that
is used to "change the language". Macros allow syntactic abstraction
the same way functions allow functional abstraction, and is almost as
important a part of the programmer's toolchest. While macros _can_ be
used to change the language in the sense of writing your own
general-purpose iteration construct or conditional operator, I believe
this is an abuse of macros, precisely because of the implications this
has for the readability of the code and for the language's user
community.

But syntactic abstractions *are* a change to the language, it just
sounds fancier.

I agree that injudicious use of macros can destroy the readability of
code, but judicious use can greatly increase the readability. So
while it is probably a bad idea to write COND1 that assumes
alternating test and consequence forms, it is also a bad idea to
replicate boilerplate code because you are eschewing macros.
 
A

Alex Martelli

Grzegorz Chrupala wrote:
...
def foo(n):
... box = [n]
... def foo(i): box[0]+=i; return box[0]
... return foo
...

It's still a hack that shows an area where Python has unnecessary
limitations, isn't it?

Debatable, and debated. See the "Rebinding names in enclosing
scopes" section of http://www.python.org/peps/pep-0227.html .

Essentially, Guido prefers classes (and instances thereof) to
closures as a way to bundle state and behavior; thus he most
emphatically does not want to add _any_ complication at all,
when the only benefit would be to have "more than one obvious
way to do it".

Guido's generally adamant stance for simplicity has been the
key determinant in the evolution of Python. Guido is also on
record as promising that the major focus in the next release
of Python where he can introduce backwards incompatibilities
(i.e. the next major-number-incrementing release, 3.0, perhaps,
say, 3 years from now) will be the _elimination_ of many of
the "more than one way to do it"s that have accumulated along
the years mostly for reasons of keeping backwards compatibility
(e.g., lambda, map, reduce, and filter, which Guido mildly
regrets ever having accepted into the language).


The rule Python currently use to determine whether a variable
is local is maximally simple: if the name gets bound (assigned
to) in local scope, it's a local variable. Making this rule
*any* more complicated (e.g. to allow assignments to names in
enclosing scopes) would just allow "more than one way to do
it" (making closures a viable alternative to classes in more
cases) and therefore it just won't happen. Python is about
offering one, and preferably only one, obvious way to do it,
for any value of "it". And another key principle of the Zen
of Python is "simple is better than complex".

Anybody who doesn't value simplicity and uniformity is quite
unlikely to be comfortable with Python -- and this should
amply answer the question about the motivations for reason
number 1 why the above foo is unacceptable in Python (the
lambda's body can't rebind name n in an enclosing scope).

Python draws a firm distinction between expressions and
statements. Again, the deep motivation behind this key
distinction can be found in several points in the Zen of
Python, such as "flat is better than nested" (doing away
with the expression/statement separation allows and indeed
encourages deep nesting) and "sparse is better than dense"
(that 'doing away' would encourage expression/statements
with a very high density of operations being performed).

This firm distinction should easily explain other reasons
why the above foo is unacceptable in Python: n+=i is a
statement (not an expression) and therefore it cannot be
held by a 'return' keyword; 'return' is a statement and
therefore cannot be in the body of a 'lambda' keyword.

And this touches on yet another point of the Zen of Python:
explicit is better than implicit. Having a function
implicitly return the last expression it computes would
violate this point (and is in fact somewhat error-prone,
in my experience, in the several languages that adopt
this rule).

Somebody who is unhappy with this drive for explicitness,
simplicity, uniformity, and so on, cannot be happy with
Python. If he wants a very similar language from most
points of view, BUT with very different philosophies, he
might well be quite happy with Ruby. Ruby does away with
any expression/statement distinction; it makes the 'return'
optional, as a method returns the last thing it computes;
it revels in "more than one way to do it", clever and cool
hacks, not perhaps to the extent of Perl, but close enough.

In Ruby, the spaces of methods and data are separate (i.e.,
most everything is "an object" -- but, differently from
Python, methods are not objects in Ruby), and I do not
think, therefore, that you can write a method that builds
and returns another method, and bind the latter to a name --
but you can return an object with a .call method, a la:

def outer(a) proc do |b| a+=b end end

x = outer(23)
puts x.call(100) # emits 123
puts x.call(100) # emits 223

[i.e., I can't think of any way you could just use x(100)
at the end of such a snippet in Ruby -- perhaps somebody
more expert of Ruby than I am can confirm or correct...?]
but apart from this it seems closer to what the above
quotes appear to be probing for. In particular, it lets
you be MUCH, MUCH denser, if that is your purpose in life,
easily squeezing that outer function into a (short) line.
Python is NOT about making code very dense, indeed, as
above mentioned, it sees _sparseness_ as a plus; a typical
Pythonista would cringe at the density of that 'outer'
and by contrast REVEL at the "sparsity" and "explicitness"
(due to the many names involved:) of, e.g.:

def make_accumulator(initial_value):
accumulator = Bunch(value=initial_value)
def accumulate(addend):
accumulator.value += addend
return accumulator.value
return accumulate

accumulate = make_accumulator(23)
print accumulate(100) # emits 123
print accumulate(100) # emits 223


(using the popular Bunch class commonly defined as:
class Bunch(object):
def __init__(self, **kwds):
self.__dict__.update(kwds)
). There is, of course, a cultural gulf between this
verbose 6-liner [using an auxiliary class strictly for
reasons of better readability...!] and the terse Ruby
1-liner above, and no doubt most practitioners of both
languages would in practice choose intermediate levels,
such as un-densifying the Ruby function into:


def outer(a)
proc do |b|
a+b
end
end

or shortening/densifying the Python one into:

def make_accumulator(a):
value = [a]
def accumulate(b):
value[0] += b
return value[0]
return accumulate

but I think the "purer" (more extreme) versions are
interesting "tipizations" for the languages, anyway.


Alex
 

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,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top