Python syntax in Lisp and Scheme

P

prunesquallor

Marcin 'Qrczak' Kowalczyk said:
Note that Lisp and Scheme have a quite unpleasant anonymous function
syntax, which induces a stronger tension to macros than in e.g. Ruby or
Haskell.

Good grief!

Unpleasant is the inner classes needed to emulate anonymous functions
in Java.
 
P

prunesquallor

Raffael Cavallaro said:
Actually, I think that any anonymous function syntax is undesirable. I
think code is inerently more readable when functions are named,
preferably in a descriptive fashion.

So it'd be even *more* readable if every subexpression were named as
well. Just write your code in A-normal form.
I think it is the mark of functional cleverness that people's code is
filled with anonymous functions. These show you how the code is doing
what it does, not what it is doing.

I disagree. This:

(map 'list (lambda (x) (+ x offset)) some-list)

is clearer than this:

(flet ((add-offset (x) (+ x offset)))
(map 'list #'add-offset some-list))
 
P

Pascal Costanza

Stephen said:
This would make some sense. After all, 'head' and 'tail' actually
imply some things that are not always true. Those 'cons' thingies may
be trees rather than lists, and even if they are lists they could be
backwards (most of the items under the 'car' side with only one item
on the 'cdr' side) which is certainly not what I'd expect from 'head'
and 'tail'.

I think that's the essential point here. The advantage of the names car
and cdr is that they _don't_ mean anything specific. I wouldn't mind if
they were called jrl and jol, or rgk and rsk, etc. pp.

This is similar to how array elements are accessed. In expressions like
a[5], the number 5 doesn't mean anything specific either.


Pascal
 
P

Paul Foley

|On Wed, Oct 08, 2003 at 03:59:19PM -0400, David Mertz wrote:
|> |Come on. Haskell has a nice type system. Python is an application of
|> |Greespun's Tenth Rule of programming.
|> Btw. This is more nonsense. HOFs are not a special Lisp thing. Haskell
|> does them much better, for example... and so does Python.
|Wow. The language with the limited lambda form, whose Creator regrets
|including in the language, is ... better ... at HOFs?
|You must be smoking something really good.
I guess a much better saying than Greenspun's would be something like:
"Those who know only Lisp are doomed to repeat it (whenver they look at
another language)." It does a better job of getting at the actual
dynamic.

Is there anyone who knows only Lisp?

Those who know Lisp repeat it for a reason -- and it isn't because
it's all they know! [Besides, Greenspun's 10th isn't about _Lispers_
reinventing Lisp; it's about _everybody else_ reinventing Lisp]
In point of fact, Python could completely eliminate the operator
'lambda', and remain exactly as useful for HOFs. Some Pythonistas seem
to want this, and it might well happen in Python3000. It makes no
difference... the alpha and omega of HOFs is that functions are first
class objects that can be passed and returned. Whether they happen to
have names is utterly irrelevant, anonymity is nothing special.

True enough. Naming things is a pain though. Imagine if you couldn't
use numbers without naming them: e.g., if instead of 2 + 3 you had to
do something like

two = 2
three = 3
two + three

Bleargh! It "makes no difference" in much the same way that using
assembler instead of Python "makes no difference" -- you can do the
same thing either one, but one way is enormously more painful.

[Mind you, Python's lambda is next to useless anyway]
 
A

Andrew Dalke

(I know, I swore off cross-posting on this topic, but the claims
made here were too outrageous for me to ignore)

I wish to show that local information is insufficient for cutting and
pasting under some circumstances.

Absolutely correct, but rarely the source of errors. When it does
occur it is almost always immediately after the cut&paste and so
the context is fresh in the mind of the who did the c&p. The chance
for it to appear in wild code is minute. I can't recall ever coming
across it.
Consider this thought experiment: pick a character (like parenthesis
for example) go to a random line in a lisp file and insert four of them.
Is the result syntactically correct? No.

You're surely exaggerating here. Here's your factorial example,
which has not been cleaned up.

(defun factorial (x)
(if (> x 0)
x
(*
(factorial (- x 1))
x
)))

I'll insert four "a" characters in the function name

(defun aaaafactorial (x)
(if (> x 0)
x
(*
(factorial (- x 1))
x
)))

Is that not syntactically correct? For that matter, what if
I insert four spaces, like

(defun factorial (x)
(if (> x 0)
x
(*
(fact orial (- x 1))
x
)))

or insert four quotes

(defun factorial (x)
(if (> x 0)
x
''''(*
(factorial (- x 1))
x
)))

However, there is a class of *syntactic* error that is possible in
python, but is not possible in lisp

Here's a class of error possible in Lisp and extremely hard to get
in Python -- omitting a space between an operator and an operand
causes an error

Valid Lisp: (- x 1)
Invalid Lisp: (-x 1) ; ;well, not invalid but semantically different

Valid Python: x - 1
Valid Python: x-1

Yes, I know, it's because '-x' isn't an operator. Tell it to the
beginning programmer who would have these problems in Python.

Here's another class of error you can get in Lisp but is hard to
get in Python (except in a string). Randomly insert a '

Valid Lisp: (- x 1)
Valid Lisp: '(- x 1) ;;; but semantically very different

Will the same beginning user you talk about for Python be
able to identify a single randomly inserted quote in Lisp?
Now go to a random line in a python file and insert four spaces. Is
the result syntactically correct? Likely. Could a naive user find
them? Unlikely. Could you write a program to find them? No.

I tried writing code to test this automatically but ran into problems
because I inserting four spaces at the start of a line may be syntactically
correct and may also not affect the semantics. For example

def spam(x = some_value,
y = some_other_value):

Adding 4 characters to the start of the 2nd line doesn't change the
meaning of the line.

There definitely were silent errors, like changing returns of
the sort

if x > 0:
return "positive"
return "nonpositive"

into

if x > 0:
return "positive"
return "nonpositive"

NB: This should be caught in any sort of unit test. The real test
is if it's hard to detect by a programmer; I'm not the one to
answer that test since I'm too used to Python. I suspect it is
generally easy to find, especially when the code is actually run,
but that it isn't always automatic.

I also figured given the quote counter example it wasn't
worthwhile categoring everything by hand.
Delete four adjacent spaces in a Python file. Will it still compile?
Likely.

Here's test code. Note that I give each file 30 chances to succeed
after deleting 4 spaces, and it *never* did so. That surprised me as
I thought some of the hits would be in continuation blocks. There
may be a bug in my test code so I submit it here for review.

=================
import string, random, os

def remove_random_4_spaces(s):
x = s.split(" ")
if len(x) <= 1:
# needed for re.py which doesn't have 4 spaces
return "]]" # force a syntax error
i = random.randrange(1, len(x))
x[i-1:i+1] = [x[i-1] + x]
return " ".join(s)

def check_for_errors(filename):
s = open(filename).read()
for i in range(30):
t = remove_random_4_spaces(s)
try:
exec t in {}
print "Success with", filename, "on try", i
return 0
except SyntaxError:
pass
return 1

def main():
dirname = os.path.dirname(string.__file__)
filenames = [name for name in os.listdir(dirname)
if name.endswith(".py")]
count = 0
errcount = 0
problems = []
for filename in filenames:
print filename,
err = check_for_errors(os.path.join(dirname, filename))
if err:
errcount += 1
print "properly failed"
else:
print "still passed!"
problems.append(filename)
count += 1
print errcount, "correctly failed of", count
print "had problems with", problems

if __name__ == "__main__":
main()


=================
anydbm.py properly failed
asynchat.py properly failed
asyncore.py properly failed
atexit.py properly failed
audiodev.py properly failed
base64.py properly failed
...
__future__.py properly failed
__phello__.foo.py properly failed
185 correctly failed of 185
had problems with []


Andrew
(e-mail address removed)
 
E

Erann Gat

[A theory of with-maintained-condition]
Erann: is my understanding correct?

Yep. There are many plausible ways to implement
with-maintained-condition, and that's one of them.

E.
 
J

Jacek Generowicz

Daniel P. M. Silva said:
Please let me know if you hear of anyone implementing lisp/scheme in
Python :)

Nah, I really don't want to hear about yet another personal Lisp
implementation. (Besides, somewhere, Alex Martelli implements a cons
cell as an example extension module, and that's half of Lisp done
already :)

However, please _do_ tell me if you hear of anyone implementing Python
in Lisp[*].

Having Python as a front-end to Lisp[*] (as it is now a front-end to
C, C++ and Java) would be very interesting indeed.


[*] Common Lisp please.
 
K

ketil+news

Raffael Cavallaro said:
I think it is the mark of functional cleverness that people's code is
filled with anonymous functions. These show you how the code is doing
what it does, not what it is doing.

Uh, I often use a lambda because I think it improves readability.

I could write

(,) x . length . fst

but think

\(a,_) -> (x,lenght a)

is clearer, because it is *less* functionally clever. Of course, it
could be written

let pair_x_and_length_of_first (a,_) = (x,lenght a)
in pair_x_and_length_of_first

but I don't think it improves things, and in fact reduces lucidity and
maintainability. Naming is like comments, when the code is clear
enough, it should be minimized. IMHO.

-kzm
 
T

Terry Reedy

Paul Foley said:
True enough. Naming things is a pain though. Imagine if you couldn't
use numbers without naming them: e.g., if instead of 2 + 3 you had to
do something like

two = 2
three = 3
two + three

For float constants in a language (such as Fortran) with multiple
float types (of different precisions), naming as part of a declaration
of precision is (or at least has been) a standard practice in some
circles. It makes it easy to change the precision used throughout a
program.
[Mind you, Python's lambda is next to useless anyway]

It is quite useful for its designed purpose, which is to abbreviate
and place inline short one-use function definitions of the following
pattern: def _(*params): return <expression using params>.

However, making the keyword 'lambda' instead of something like 'func'
was a mistake for at least two reasons:
1) it confuses those with no knowledge of lambda calculus and for whom
it is an strange, arbitrary term, possibly conjuring images of little
sheep, rather than being familar and mnemonic;
2) it raises unrealistic expectations in who know of 'lambda' as an
anonymous version of 'defun' (or whatever), leading them to make
statements such as the above.

Terry J. Reedy
 
P

Paul Rubin

Jacek Generowicz said:
However, please _do_ tell me if you hear of anyone implementing Python
in Lisp[*].

Having Python as a front-end to Lisp[*] (as it is now a front-end to
C, C++ and Java) would be very interesting indeed.

That would not be simple to do, because of weird Python semantics.
But doing a Python dialect (a little bit different from CPython) would
be worthwhile.
 
R

Raffael Cavallaro

(flet ((add-offset (x) (+ x offset)))
(map 'list #'add-offset some-list))

But flet is just lambda in drag. I mean real, named functions, with
defun. Then the code becomes:

(add-offset the-list)

instead of either of theversions you gave. The implementation details of
add-offset are elswere, to be consulted only when needed. They don't
interrupt the flow of the code, or the reader's understanding of what it
does. If you need that optimization, you can always throw in (declaim
(inline 'add-offset)) before add-offset's definition(s).

I guess I'm arguing that the low level implementation details should not
be inlined by the programmer, but by the compiler. To my eye, anonymous
functions look like programmer inlining.
 
D

David Eppstein

Jacek Generowicz said:
However, please _do_ tell me if you hear of anyone implementing Python
in Lisp[*].

Having Python as a front-end to Lisp[*] (as it is now a front-end to
C, C++ and Java) would be very interesting indeed.

It is now also a front-end to Objective C, via the PyObjC project.
 
T

Terry Reedy

Thank you for the clarification. The message for me is this: a
Python-aware editor should have an option to keep a pasted-in snippet
selected so that the indentation can be immediately adjusted by the
normal selected-block indent/dedent methods without having to
reselect.

TJR
 
T

Terry Reedy

I disagree. This:

(map 'list (lambda (x) (+ x offset)) some-list)

is clearer than this:

(flet ((add-offset (x) (+ x offset)))
(map 'list #'add-offset some-list))

I agree for this example (and for the Python equivalent). But if the
function definition is several lines, then I prefer to have it defined
first so that the map remains a mind-bite-sized chunk.

TJR
 
T

Thant Tessman

Raffael said:
[...] Actually, I think that any anonymous function syntax is undesirable. I
think code is inerently more readable when functions are named,
preferably in a descriptive fashion. [...]

Before the invention of higher-level languages like Fortran, the
programmer was burdened with the task of naming every intermediate value
in the calculation of an expression. A programmer accustomed to the
functional style finds the need in non-FP languages to name every
function analogously awkward.

-thant
 
J

Joe Marshall

Raffael Cavallaro said:
But flet is just lambda in drag. I mean real, named functions, with
defun. Then the code becomes:

(add-offset the-list)

I'm assuming that offset is lexically bound somewhere outside
the let expression so that I have to capture it with an in-place
FLET or LAMBDA. If we had an external add-offset then I'd have
to do something like

(add-offset offset some-list)

The problem with this is that you've essentially outlawed MAP.

(map 'list (make-adder offset) some-list)

The problem with this is that MAKE-ADDER is no less a lambda in drag,
and it isn't even local.
 
D

Dave Benjamin

Before the invention of higher-level languages like Fortran, the
programmer was burdened with the task of naming every intermediate value
in the calculation of an expression. A programmer accustomed to the
functional style finds the need in non-FP languages to name every
function analogously awkward.

Well put, Thant. Thank you.
 
H

Hartmann Schaffer

...
I think that's the essential point here. The advantage of the names car
and cdr is that they _don't_ mean anything specific.

gdee, you should read early lisp history ;-). car and cdr ha[d|ve] a
very specific meaning

hs
 
H

Hartmann Schaffer

...

For float constants in a language (such as Fortran) with multiple
float types (of different precisions), naming as part of a declaration
of precision is (or at least has been) a standard practice in some
circles. It makes it easy to change the precision used throughout a
program.

this is a good practice when the literals are parameters that you want
to change occasionally. other reasons why you want to do that is that
typing certain constants (e.g. pi or e) is error prone, so writing
them down once and binding them to a (descriptive) name is preferrably
to having to type them repeatedly. but unless they play a parameter
role, binding short literals to name doesn't serve any purpose
[Mind you, Python's lambda is next to useless anyway]

It is quite useful for its designed purpose, which is to abbreviate
and place inline short one-use function definitions of the following
pattern: def _(*params): return <expression using params>.

the last version of python i used was 1.5.x, and then the absence of
closures made the anonymous functions pretty useless

hs
 
H

Hartmann Schaffer

But flet is just lambda in drag. I mean real, named functions, with
defun. Then the code becomes:

(add-offset the-list)

instead of either of theversions you gave.

the version he gave has the advantage that it doesn't clutter up the
namespace of the environment. with

(map (lambda (x) (+ x offset)) the-list)

you have everything that is relevant in the immediate neighborhood of
the statement. with a separate defun you have to search the program
to see what the function does. i agree that for lengthy functions
defining it separately and just writing the name is preferrable.
...
I guess I'm arguing that the low level implementation details should not
be inlined by the programmer, but by the compiler. To my eye, anonymous
functions look like programmer inlining.

i would call this a misconception

hs
 

Ask a Question

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

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

Ask a Question

Members online

Forum statistics

Threads
474,169
Messages
2,570,920
Members
47,463
Latest member
FinleyMoye

Latest Threads

Top