merits of Lisp vs Python

?

=?ISO-8859-1?Q?Andr=E9_Thieme?=

Paul said:
No they're part of Python syntax. A change in indent level is
recognized by Python's lexical scanner as a token and you should count
it. You wouldn't count the indent and \n separately though.


It gets worse when the expressions are nested.

So instead of
cmp(foo(a, b, c), bar(x, y, z)) you would prefer
foo(a, b, c) cmp bar(x, y, z) ?

for x in 0 range len(s): ...

Maybe make single-digit functions postfix?

for x in 0 range (s)len: ...

Well, it's not really in the Pythonic style to add obscurity like
that, but you could certainly write something like:

def aif(func):
it = func()
if it: use(it)

aif(timeConsumingCalculation)

This isn't doing the same. This hardencodes the call to use() inside
aif.

if timeConsumingCalculation():
foo(it)
bar(it)

But "if" is already taken for the standard if.
Instead we add a new keyword to Python, "aif".
Then we can say:

aif timeConsumingCalculation():
foo(it)
bar(it)

You can pass anonymous functions around etc, but it will become
ugly - no Pythoniast would ever do it in production code.
If there was an aif built into Python we would find it every
now and then.


André
--
 
P

Paul Rubin

André Thieme said:
So instead of
cmp(foo(a, b, c), bar(x, y, z)) you would prefer
foo(a, b, c) cmp bar(x, y, z) ?

Don't be silly. Some operators are more natural as infix and others
as functions. It's just like in natural language. People have an
innate ability to make such distinctions and it's fine for a
programming language to use it.
 
K

Kirk Sluder

Don't be silly. Some operators are more natural as infix and others
as functions. It's just like in natural language. People have an
innate ability to make such distinctions and it's fine for a
programming language to use it.

I don't think you can really make this case. In English, German and
Spanish noun-verb order is flexible to accommodate differences in
mood, tense, and emphasis:
"Add 2 and 2." imperative.
"The sum of 2 and 2" noun phrase.
"2 was added to 2." passive voice.

Personally, I've always preferred use the imperative to describe
basic math rather than the passive. This would seem to map better to
RPN than infix.

And on top of that, some languages are even more flexible in regard
to word order, preferring to communicate relationships through the
modification of noun stems. In such a language, "He* her- the ring+
gave," is just as valid as "Her- the ring+ gave he*" and "He* gave
her- the ring+." ("+/-/*" signifying modifications which do not
exist in modern English.)

At any rate, I find this argument to be weak given that English
doesn't always use the same word order. And English is just one
language out of hundreds.
 
P

Paul Rubin

Kirk Sluder said:
Personally, I've always preferred use the imperative to describe
basic math rather than the passive. This would seem to map better to
RPN than infix.

For writing down complicated, nested expressions too? That's very
unusual. E.g.

n! = (n/e)**n * sqrt(2*pi*n) * (1 + (1/12n)) * ...

vs. the same thing in Lisp notation, and that's not even so complicated.
 
H

Hendrik van Rooyen

greg said:
I once heard mention of a system of units in use at
one time with the odd feature that capacitance came
out in units of length.

Picture the scene: Hobbyist walks into Dick Smith
store and says "I'd like a 5cm capacitor, please."

This is correct - think of it as the number of electrons that
can dance on the surface of a sphere of radius r.

So your hobbyist will be handed a copper ball of 10cm
diameter...

Seriously, in electrostatic units, capacitance is measured
in length. (or it was when they were trying to teach me
Physics)

- Hendrik
 
G

greg

Ken said:
McCarthy: "Is code also data in Python?"
Norvig: "No."

I don't think that was the right answer. He should have
said "Yes", and then shown McCarthy eval() and exec.

Code isn't quite as *convenient* to work with as data
in Python as it is in Lisp, but that doesn't mean it
can't be done.

(I agree that the proffered example was not an instance
of "code is data", though.)
 
K

Ken Tilton

greg said:
I don't think that was the right answer.

Norvig is a smart guy. He was talking to John McCarthy. He gave the
right answer. :)
He should have
said "Yes", and then shown McCarthy eval() and exec.

No, in those cases the Python interpreter is still dealing with the
code, not the user's application code. As a meta-developer (to coin a
word) I want to be able to write code that takes apart other code I
write to do useful things with it /without writing a parser for my
chosen language/. I just found a bug in ACL that might be instructive.

The defskill macro I have been writing might be coded like this:

(defskill abs-value
(category "Algebra I" "Real Numbers")
....)

(I am kinda mirroring defclass here, which looks like:

(defclass <name> <superclasses>*
(<slot-defs>*)
...and then optional sub-specs each keyed by the first symbol..
:)documentation "chya")
:)metaclass...)
:)default-initargs)))

Come to think of it, I might have made /my/ options :keywords just to
make the resemblance stronger (and yes I am digressing into the bit
about how Lispniks by convention stay recognizable with new syntax).

Anyway, I decided to do a little auto QA and add an assertion to make
sure I did not inadvertently leave out the category option (long story,
I am not usually so anal), so defskill starts like this:

(defmacro defskill (id &body skill-info)
(assert (find 'category skill-info :key 'car))...

That is just normal list code. The macro signature destructures the code
I write inside the defskill invocation into an id and a list of
following code ("&body skill-info" says "bind the rest of the code to
the local variable skill-info as a list"). the (find ... :key 'car)
walks down the list taking the car (first) of each list and comparing
against that which is being sought. Simple ol' Lisp.

I then expand the (somewhat) validated data/code:

(loop with sub-id = id
for (q . q-def) in skill-info
collecting
(ecase q
(category <return desired Lisp code>)
...etc...)

q for "quality" or "attribrute". Not "o" for option. :) Again, using
standard loop destructuring on the option code/data.

Y'all need to get over it: we really are writing code that eats
code/data to produce code. Unaided by any parser other than the same
destructuring mechanisms we use on data.

ken

ps. I forgot the bug. I coded in the end:

(defskill |Absolute Value| ...etc..)

Where |absolute value| should be treated as a single symbol, and is by
the ACL compiler. An interactive macroexpand, however, misses that that
is one symbol and defskill (if you followed the Lisp) dies trying to
take the car of the atom VALUE (a symbol). k
 
G

greg

Ken said:
I think your brain is stuck on flaming.

Sorry, I didn't mean to come across as flaming, far
from it. I'll take a deep breath before posting from
now on, I promise. :)
I did explain the last little fun bit (where reverse code miraculously
got a case-specific "signed-value" parameter bound to exactly the right
bit of math structure).

I didn't mention that because it was addressed by
another poster. The signature of the user's reverse
function can be made extremely flexible if you have
the foresight to define it as something like

def reverse(resx, opnd, **kwds):
...

Then you can later change it to

def reverse(resx, opnd, signed_value, **kwds):
...

and any existing reverse functions will just absorb
and ignore the extra argument.

However, rather than add an ever-increasing number
of arguments to the signature, I think I would do it
a different way: pass a single object with attributes.
For the want of a better name, let's call it "env"
for "environment". The signature is then

def reverse(env):
...

and the body can refer to env.resx, env.opnd,
env.signed_value, or whatever else is required.

If this still doesn't cover the requirements, please
explain and I'll try to adapt it accordingly.
 
G

greg

André Thieme said:
(aif (timeConsumingCalculation)
(use it))

I think the answer is that you just wouldn't do
that in Python at all. Having magic variables
spring into existence in your local namespace
as a side effect of calling something is just
not Pythonic. (It is very Perlish, on the other
hand.)

The closest you might come is using the new
"with" statement like this:

with aif(timeConsumingCalculation()) as it:
use(it)

where the object returned by aif(x) has an
__enter__ method that raises an exception which
aborts the whole with statement if x is None,
thus avoiding executing the body. But that's
so horribly convoluted that any sane programmer
would just write it out the straightforward
way to begin with.
So in some languages that support functional programming one needs to do
extra work to get lazyness, while in Haskell one gets extra work if one
doesn't want it.

Sort of, but it's more so that you can write code
that does things like I/O in what looks like a
procedural style, even though it's still purely
functional. Read about "monads" if you want to
know more -- it's truly mind-expanding (and
possibly head-exploding:) stuff...
 
G

greg

André Thieme said:
The thing with most (if not all) programming languages other than Lisp
is: these abstractions are leaky.
They have a low level knowledge leak. Users of aif need to know how to
pass in arguments. As you see he had do embed the code that needs execution
into a block (anon function) and then use this special syntax for x.

Yes, but all the standard looping constructs, etc.
in Ruby are expressed like this too. So it's
perfectly consistent with the rest of the language.
It's not something new that the user needs to learn
just to use this function.

This is even more evident in Smalltalk, where
*all* control structures, without exception, are
expressed in terms of code blocks, possibly
with parameters. And Smalltalk has no macros,
either, btw.
 
G

greg

André Thieme said:
So instead of
cmp(foo(a, b, c), bar(x, y, z)) you would prefer
foo(a, b, c) cmp bar(x, y, z) ?

Incidentally, someone once came up with an fiendishly
clever hack that lets you write things in Python like

foo(a, b, c) |kmp| bar(x, y, z)

i.e. effectively define your own infix operators. And
it doesn't even require a macro. :)

(Finding the trick to implementing this is left as an
exercise for the googler.)
 
G

greg

Then bring in more statement and you either have to
remember precedence rules (I can never remember those) or add some
parenthesis (oops!).

Probably you've been burned by C, which has a
ridiculously large number of precedence levels --
I don't know *anyone* who can remember them all.
Most people just remember a subset and use
parens for everything else.

Pascal had four levels, which was easy to remember
but not really enough. About six or seven seems
optimal to me.

Python, incidentally, has rather too many (it
seems to have caught this disease from C).
 
G

greg

André Thieme said:
The <=> is called cmp in Python.
In Lisp it is called signum. The Lisp function has in general the
advantage that it keeps type information.
While Pythons cmp returns either -1, 0 or 1 the Lisp version can
also return -1.0 and 1.0 and also complex numbers:
(signum #C(10 4)) => #C(0.9284767 0.37139067)

Unless you can use it to compare arbitrary types
such as two strings or two lists, it's not really
the same thing as Python's cmp.

Python 2.3 (#1, Aug 5 2003, 15:52:30)
[GCC 3.1 20020420 (prerelease)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> cmp("hello", "world") -1
>>> cmp([1,3], [1,2])
1
 
K

Ken Tilton

greg said:
I didn't mention that because it was addressed by
another poster. The signature of the user's reverse
function can be made extremely flexible if you have
the foresight to define it as something like

def reverse(resx, opnd, **kwds):
...

Then you can later change it to

def reverse(resx, opnd, signed_value, **kwds):
...

and any existing reverse functions will just absorb
and ignore the extra argument.

However, rather than add an ever-increasing number
of arguments to the signature, I think I would do it
a different way: pass a single object with attributes.
For the want of a better name, let's call it "env"
for "environment". The signature is then

def reverse(env):
...

and the body can refer to env.resx, env.opnd,
env.signed_value, or whatever else is required.

Looks promising. How does a generic engine that sees only a solution (a
list of mathematical expressions and for each the transformations,
results, and opnds logged by individual TF functions) build up this
environment such that it has named attributes such as signed-value?
Assume that it can examine all those opnds and results looking for
tagged values such that it then knows the name of those values that have
been named.

ken

--
Algebra: http://www.tilton-technology.com/LispNycAlgebra1.htm

"Well, I've wrestled with reality for thirty-five
years, Doctor, and I'm happy to state I finally
won out over it." -- Elwood P. Dowd

"I'll say I'm losing my grip, and it feels terrific."
-- Smiling husband to scowling wife, New Yorker cartoon
 
K

Kay Schluehr

Ken said:
Looks promising. How does a generic engine that sees only a solution (a
list of mathematical expressions and for each the transformations,
results, and opnds logged by individual TF functions) build up this
environment such that it has named attributes such as signed-value?

Most likely it doesn't since there is no such engine. Instead local
rules and combinators are encoded in classes. Hence there is nothing
but an object tree and actions are threaded through recursive method
calls.

This implies that the generic reverse function is just the dual of a
method call:

def reverse(expr):
return expr.reverse()

What expr does depends on the internal representation encoded in the
class of expr. This also implies that not only the form of the
expression is significant but also its ( dynamic ) type.
 
C

Christophe Cavalaria

Paul said:
For writing down complicated, nested expressions too? That's very
unusual. E.g.

n! = (n/e)**n * sqrt(2*pi*n) * (1 + (1/12n)) * ...

vs. the same thing in Lisp notation, and that's not even so complicated.

Yes, infix is better ( and note that Math uses primarly infix notation for
common operators ) because you have a clear view of the evaluation tree
that way. With prefix or postfix notation, you need to parse the full
expression to find what goes to the first parameter of that +, and what
goes to the second parameter.

I would say that prefix/postfix is somewhat easier to write, but that infix
is far far far easier to read.
 
J

Juan R.

greg ha escrito:
I don't know about the other Pythonistas in this
discussion, but personally I do have experience with
Lisp, and I understand what you're saying. I have
nothing against Lisp parentheses, I just don't agree
that the Lisp way is superior to the Python way in
all respects, based on my experience with both.

Parentheses have two advantages over identation. First is they can be
used both inline and block.

The inline (a b (c d (e f g))) needs to be formatted in several Python
lines. Therein SLiP (Python) got problems for inline mixed markup but
LML (LISP) or SXML (Scheme) do not.

Second is that i do not need special editors for writting/editting. It
is more difficult to do identation and editing by hand in 'Notepad'
that adding parens from keyboard [*].

[*] Tip. Type always () and next move your cursor inside ( _ ). You
never forget a closing ) this way!
 
M

metawilm

Paul said:
a = 'hello'
a[0] = 'H' # attempt to change first letter to upper case

As CLPython mirrors Python semantics, this results in a TypeError. The
internal representation of an immutable Python string is a mutable Lisp
string, but there is no way you can actually modify it from within CLPython.

How do you manage that? The compiler can't check. Is there some kind
of special dispatch on the assignment statement instead of turning it
into a setf?

Indeed, the assignment of subitems is dynamically dispatched, and
always goes via __setitem__ methods, as specified in the language. The
__setitem__ methods for strings and tuples raise exceptions. The ones
for lists and vectors could be translated into a simple (setf (aref ..)
...) and (setf (nth ..) ..), unless the index is actually a slice and
actually a subsequence must be replaced.

Now, the above description using __setitem__ methods is how it
apparently works, as observed from the outside. Internally there is a
generic function (setf py-subs) that does the work itself, skipping the
lookup and calling of __setitem__, if the object is a vector or dict.
(The user can call x.__setitem__ explicitly, so that method must exist
internally.)

Maybe you were thinking that CLPython, given Python code, outputs a big
pile of self-contained equivalent Lisp code that can be run
independently, and then CLPython is finished. Instead, only a small
amount of Lisp code is generated, but that code can only be run when
the CLPython environment is loaded. The generated code refers to that
environment, e.g. s[0] = 'x' is translated into a call to (setf
(py-subs ..) ..), and function (setf py-subs) is part of the CLPython
environment that must be loaded at run-time.

If you compile a Python function, the result is a (mostly) normal Lisp
function - its origin from the Python world does not really matter.
Also, all Python data structures are first-class Lisp data. Thus
CLPython objects live happily together with "normal" Lisp objects in
the same image.

And now we arrive at the most exciting part: other Lisp libraries can
be loaded in the same image, and we can create connections. Like, after
loading CLIM and defining how CLPython objects should be presented, we
should get a Python interpreter where classes are displayed graphically
and where you can inspect and modify them by mouse. Imagine that you
have interactively added a method to a class by dragging a function to
a class, then being able to right-click and select "write method",
which will write the definition of the method in the right place in the
class definition source code in your Climacs (CLIM-based Emacs) buffer.

That's the kind of features I have in mind, and the best thing is that
conceptually a lot of the work consists of connecting dots that already
out there. But as there are so many of them, a few extra pencils would
be quite welcome <wink>

- Willem
 
?

=?ISO-8859-15?Q?Andr=E9_Thieme?=

greg said:
I think the answer is that you just wouldn't do
that in Python at all. Having magic variables
spring into existence in your local namespace
as a side effect of calling something is just
not Pythonic. (It is very Perlish, on the other
hand.)

The closest you might come is using the new
"with" statement like this:

with aif(timeConsumingCalculation()) as it:
use(it)

where the object returned by aif(x) has an
__enter__ method that raises an exception which
aborts the whole with statement if x is None,
thus avoiding executing the body. But that's
so horribly convoluted that any sane programmer
would just write it out the straightforward
way to begin with.

Sounds like "Blub" to me:
http://www.paulgraham.com/avg.html

I will quote some parts of it:

"You can see that machine language is very low level. But, at least as a
kind of social convention, high-level languages are often all treated as
equivalent. They're not. Technically the term "high-level language"
doesn't mean anything very definite. There's no dividing line with
machine languages on one side and all the high-level languages on the
other. Languages fall along a continuum [4] of abstractness, from the
most powerful all the way down to machine languages, which themselves
vary in power.

[...]

Programmers get very attached to their favorite languages, and I don't
want to hurt anyone's feelings, so to explain this point I'm going to
use a hypothetical language called Blub. Blub falls right in the middle
of the abstractness continuum. It is not the most powerful language, but
it is more powerful than Cobol or machine language.

And in fact, our hypothetical Blub programmer wouldn't use either of
them. Of course he wouldn't program in machine language. That's what
compilers are for. And as for Cobol, he doesn't know how anyone can get
anything done with it. It doesn't even have x (Blub feature of your
choice).

As long as our hypothetical Blub programmer is looking down the power
continuum, he knows he's looking down. Languages less powerful than Blub
are obviously less powerful, because they're missing some feature he's
used to. But when our hypothetical Blub programmer looks in the other
direction, up the power continuum, he doesn't realize he's looking up.
What he sees are merely weird languages. He probably considers them
about equivalent in power to Blub, but with all this other hairy stuff
thrown in as well. Blub is good enough for him, because he thinks in
Blub.

When we switch to the point of view of a programmer using any of the
languages higher up the power continuum, however, we find that he in
turn looks down upon Blub. How can you get anything done in Blub? It
doesn't even have y.

By induction, the only programmers in a position to see all the
differences in power between the various languages are those who
understand the most powerful one. (This is probably what Eric Raymond
meant about Lisp making you a better programmer.) You can't trust the
opinions of the others, because of the Blub paradox: they're satisfied
with whatever language they happen to use, because it dictates the way
they think about programs."



André
--
 

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
474,432
Messages
2,571,682
Members
48,796
Latest member
Greg L.

Latest Threads

Top