merits of Lisp vs Python

G

Guest

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.

As I said earlier: if there are three formulas in your code, then it
doesn't matter too much.
If you write more then use infix in Lisp. It even looks better than Python:

[7x₆ + 9π³ - 6ˣ]

or

(if [√2 ≈ 1,41]
(print "Yay"))

Or mathematical reasoning:
(proof [∃ x∈M ∀ y∈Q : x≤y])

So what?


André
--
 
S

Slawomir Nowaczyk

On Sat, 16 Dec 2006 14:09:11 +0100

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

Well, too bad for you...

#> I will quote some parts of it:
#> <snip>

#> "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 statement is, clearly, right.What I can not comprehend is how
Lispers tend to mis-read "comprehend" above as "think is the best". Some
of us *do* comprehend Lisp, understand that there are uses for macros,
just do not see the overwhelming need for them in everyday work (given
sufficiently rich language core).

I other words, people (in this sub-thread, at least) do not argue that
Python is *as powerful* as Lisp -- we understand there are things macros
can do easier/faster/more conveniently than functions or other features
Python has. Lisp *is* more powerful than Lisp. You win.

What we try to understand is why would you think Lisp is a better
programming language than Python :)

Sure, there are times I wish Python had macros. I would be able to save
a couple of keystrokes here and there. But at other times, I am glad it
does not have them, because when I read Bad Code (commonly used synonym
for "somebody else's code") I do not need to wonder what aif and 1000
others, similar things really do.

In my experience, *if* somebody really needs aif, it can be done without
macros. But without macros people will only introduce such thing if it
does save significantly more than 3 or 4 lines of code in the whole
project -- which is a good thing in my book. YMMV.

I by far prefer to have

it = timeConsumingCalculations()
if it:
use(it)

five (or even ten) times in a medium sized project than to have to
figure out what "aif" means. If the idiom is used 100 times, then
something is wrong: either system should be redesigned or introducing
"aif" is a good idea (but then Python idiom works just as good as Lisp
one). YMMV.

--
Best wishes,
Slawomir Nowaczyk
( (e-mail address removed) )

There are 2 kinds of people in the world - those that divide
the people into 2 groups, and those who don't.
 
K

Ken Tilton

Kay said:
Ken Tilton schrieb:




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.

Most likely that is the engine of which I was speaking. :) Why does the
engine consisting of "internal" methods make it not an engine? I think
you saw the word "engine" and assumed I did not understand OO design. I
feel a Naggum coming on...

kt

ps. This won't make sense unless you know about my Cells project, but
the solution to a /problem/ which has attributes expr and instructions,
is a declarative attribute of a problem. But that attribute is coded
essentially like this:

(defclass problem ()
....
(solution :accessor solution
:initform (c-formula ()
(solve (expr self) (instructions self)))))

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

--
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
 
R

Raffael Cavallaro

It never occurs to Lisp programmers that Lisp, too, might be a Blub.

Of course it does - Thats why we try ocaml and haskell etc. It's just
that we don't see the useful features of these languages as being
sufficiently useful to compensate for their lack of the ability to
easily do syntactic abstractions over a uniform syntax. There's no
question that other languages have some features that common lisp does
not (and vice versa). Lispers just can't abide being locked into a
particular paradigm because a language doesn't have the basic features
(macros and uniform syntax) necessary to provide new paradigms for
ourselves when needed or wanted.

For example, a common lisp with optional static typing on demand would
be strictly more expressive than common lisp. But, take say, haskell;
haskell's static typing is not optional (you can work around it, but
you have to go out of your way to do so); haskell's pure functional
semantics are not optional (again, workarounds possible to a limited
extent). This requires you to conceive your problem solution (i.e.,
program) within the framework of a particular paradigm. This lock-in to
a particular paradigm, however powerful, is what makes any such
language strictly less expressive than one with syntactic abstraction
over a uniform syntax.
 
J

Juan R.

Raffael Cavallaro ha escrito:
This lock-in to
a particular paradigm, however powerful, is what makes any such
language strictly less expressive than one with syntactic abstraction
over a uniform syntax.

Right, but it would be also remarked that there is not reason to
ignoring the development and implementation of specific syntaxes,
paradigms, etc. optimized to specialized (sub)fields of discourse.

If all you need is basic arithmetics and 'static' data structures,
optimization á la Python Y = a + b * c gets sense in most of cases.

If you are developing symbolic software where data structures as highly
'dinamic' and where you would wait something more than sums, divisions,
sines and cosines, and some numeric differentials then the parens
automatically gets sense.

Using LISP-like syntax for everything would be so stupid as using
quantum mechanics for billiards.
 
J

Juan R.

Using LISP-like syntax for everything would be so stupid as using
quantum mechanics for billiards.

Claiming that LISP parens are Stupid, Superfluous, or Silly just
because you do not need them in your limited field of discourse, would
be so stupid as those people thinking that just because they use
classical mechanics at the macro scale and works for them, then
classical mechanics would also work at the atomic scale [*].

[*] Even today, after 100 years some people think that quantum
mechanics is Stupid, Superfluous, or Silly and some classical
formulation will replace.
 
K

Kirk Sluder

(emphasis added)
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.

I wasn't even talking about Polish notation vs. other standard
notations. I was talking about your claimed correspondence between
infix and natural Languages.

(1/12n) - "divide 1 by the product of" of 12 and n"
sqrt(2*pi*n) - "calculate the square root of the product of 2 pi and
n."

If computer languages were to mimic natural languages on this point,
they would support both forms of expression and be sensitive to mode
and mood.
 
J

Jon Harrop

Raffael said:
Of course it does - Thats why we try ocaml and haskell etc. It's just
that we don't see the useful features of these languages as being
sufficiently useful to compensate for their lack of the ability to
easily do syntactic abstractions over a uniform syntax.

That applies to the Lispers who've tried other languages and stayed with
Lisp.
There's no
question that other languages have some features that common lisp does
not (and vice versa). Lispers just can't abide being locked into a
particular paradigm because a language doesn't have the basic features
(macros and uniform syntax) necessary to provide new paradigms for
ourselves when needed or wanted.

Why do you think that uniform syntax is necessary to provide new paradigms
when it is equivalent to infix syntax?
For example, a common lisp with optional static typing on demand would
be strictly more expressive than common lisp. But, take say, haskell;
haskell's static typing is not optional (you can work around it, but
you have to go out of your way to do so); haskell's pure functional
semantics are not optional (again, workarounds possible to a limited
extent).

In what way is Haskell's support for imperative programming limited?
This requires you to conceive your problem solution (i.e.,
program) within the framework of a particular paradigm. This lock-in to
a particular paradigm, however powerful, is what makes any such
language strictly less expressive than one with syntactic abstraction
over a uniform syntax.

Can you give an example of a Lisp macro that does something useful that you
can't do in these other languages?
 
K

Kirk Sluder

Kirk Sluder said:
If computer languages were to mimic natural languages on this point,
they would support both forms of expression and be sensitive to mode
and mood.

And ok, bringing this around to lisp, many complex expressions such
as polynomials can be viewed as lists of sub-expressions. So in this
case, the expression can be re-written as:

(* (subexp1) (subexp2) (subexp3) (subexp4))

Which is probably how I'd solve the expression if I was using paper
and pencil: simplify and combine.

And there is something that is missing here in arguing about
computer language notations in relationship to human language
readability, or correspondence to spoken language. I'm not writing
code for another human, I'm writing code for a machine. Often, the
optimum expression of an mathematical concept for a machine is
relatively baroque compared to the optimum expression for a human
being.
 
X

xscottg

Ken said:
I was hoping no one would make that mistake. :) macros are all about
code is data, but code is not data in Python* so the two words code and
data serve to differentiate them for Pythonistas.

I disagree. I frequently write data-driven algorithms in C and Python
(data is code). I occasionally write code-generators too (code is
data). Just because the representation is different between code and
data in those languages doesn't mean that you can't use data as code
(interpreter style) or generate code as data (compiler style). (I
won't bother boring you with the Python bytecode hacks or the parser
module, because I think those are not terribly practical.)

It's very elegant that Lisp makes the representations between code and
data so similar, but it looks like you know the mantra and not the
meaning if you can't apply that principle in other languages.

BTW... Even in Scheme, I have to use syntax-object->datum and it's
inverse to switch between the two. I suspect this has something to do
with the other context that must be associated with the data to turn it
into code. Probably at least the enclosing environment for lexical
scoping and the line numbers for debugging or exception handling. Does
Common Lisp maintain this information with it's macro expansions? If
so, you have a slight difference between code and data too. If not,
how do you get the line number when a problem happens in a nested
macro?

Moreover, if you really want to be pedantic about what "is" means in
"code is data", then you have to acknowledge that data is a superset of
code, since all code is obviously data, but there are plenty of types
of data that aren't used as code. Or are they? :)

* Taking questions after a keynote to ILC200? where he reiterated that
Python was the same as Lisp for all intents and purposes:

Norvig: "Yes, John?"
McCarthy: "Is code also data in Python?"
Norvig: "No."

End of exchange. :)

Really? You're relying on an appeal to authority? Ok, I'm going to
start arguing by analogy then...


yeah, and god forbid you should ask. :) this is the crux of the matter!

Actually, it is kinda cool that you and Greg are semi-identifying the
crux by saying "this is the only bit I do not get, I'll skip this, move
on, nothing to see here".

Ok, I'll bite. What does it do? I read through it, and it looks the
code you're passing to the macro does some things like calculating the
number of decimal digits and generating a 2 + a random number of that
many digits to find a divisor or something. It also looks like you
have some "string interpolation" to substitute names in your text, but
as a whole, I really don't have any clue what it's all about.

It looks like the macro itself is building some names on the fly and
defining methods (new specializations for multimethods?) with those
names.

So I'm sure I'm missing something, but there is almost certainly a
readable equivalent in Python (and even C). If you really want to
generate dynamic names for functions, you can do that in Python by
modifying the class or globals hash or whatever. A more standard way
might be to be have members in the base class.

There is even a multi-methods module in Python, but I've never used it.
I'd guess you could add to that dynamically too.

Hmmm. Actually, that is the whole point, all of Python is allowed.
decorators were used in PyCells, but I never got much of an idea what
they did. Is there a moral equivalent of a macroexpansion for decorators
so you can show the before and after?

It's Python that doesn't allow you to decorate classes. (You can
decorate functions, but not classes...) I made this comment as a
criticism of Python since it seems like a non-orthogonal corner... It
would have fit in this case.

Decorators are pretty simple:

@foo
def bar(): pass

is equivalent to:

def bar(): pass
bar = foo(bar)

The callable foo can use whatever it wants to inspect, modify, or wrap
the function bar.

but:

@foo
class bar: pass

is not currently allowed, so you have to do

class bar: pass
bar = foo(bar)


exactly what we are looking for in this cultural exchange: how would
Python handle what I am doing with macros in the reverse functions? Make
it as slick and programmer-friendly (cuz I may get to a hundred of these
before I am done with Algebra I) as possible. When all the Pythonistas
proclaim it optimal, then we compare and contrast.

This, btw, is the Tilton Test for language comparison: Not measurements
of programmer effort, rather examination of perfect equivalent code.
PyCells vs Cells would be an amazing case, because that is some hairy
functionality.

That seems like a fine strategy. I fall more on the programmer effort
side. I want to write as little code as possible in a readable way
such that it meets the requirements.

I'm not a particularly good ambassador for Python. I know Python much
better than Lisp (Scheme), but I already think Scheme is a better
language. Macros are part of that. I'm mostly playing devil's
advocate here because I don't think your example really brings it home.
I've got limited time that I'm willing to invest in learning your
algebra system, but I haven't seen (understood, if you insist) anything
yet that couldn't be done readably in Python. If you want Common Lisp
to win in your language battle against the Pythonistas, you're going to
need to break out with something stronger. Then again, you're probably
pissing into the wind anyway.


Cheers,
-Scott
 
R

Raffael Cavallaro

Why do you think that uniform syntax is necessary to provide new paradigms
when it is equivalent to infix syntax?

Because it doesn't require one to write a parser for each new syntax
for each new paradigm.
In what way is Haskell's support for imperative programming limited?

It requires one to frame everything in functional terms or to jump
through the hoop of monads.
Can you give an example of a Lisp macro that does something useful that you
can't do in these other languages?

It isn't a question of "can't do in these other languages," it's a
matter of "can't do as easily in these other languages." Look at kenny
tilton's cells. Its a dataflow paradigm built largely of macros. It
goes completely against the semantics of haskell - cells is all about
the eager evaluation of side effecting state mutation. Could you do it
in haskell? Yes, in the greenspun/turing-completeness sense, but not
nearly as easily as in common lisp, because the very paradigm - eager
evaluation combined with side effecting state mutation - goes against
the basic semantics of haskell. You'd have to jump through extra hoops
to build a system whose very nature contradicts two of the semantic
foundations of haskell - laziness and absense of side effects.

Then there's the issue of the new syntax. Easy to build in lisp without
learning another language - lisp macros use lisp. What little I've seen
of caml4p looks like perlesque line noise. Ultimately I think that the
defaults of both haskell and ocaml - functional, static typing,
non-uniform syntax - are things I don't want as defaults, but as
options for later in the development of only certain pieces of code. I
don't want to be required to jump through the pure-functional,
must-use-monads-for-any-side-effects, static-type, non-uniform-syntax
hoops to express my ideas. It makes me less flexible in dealing with
individual parts of a program differently.
 
K

Ken Tilton

I disagree.

I might agree. The suggest alternative where the engine would build up
an "environment" such that a lambda could seem to be accessing a
pre-defined slot-name reminded me of some of the magic I noticed when
trying to port Cells to Python. If the interpreter (as it seems)
gracefully handles things like that, then one could argue that Python
can at least generate code to be consumed by what I would have as a
macro body, and thus achieve a degree of code is data.

Of course then I would point out that this would be terribly confusing
and should be banned from Python for the same reason as are macros. :)

I frequently write data-driven algorithms in C and Python
(data is code). I occasionally write code-generators too (code is
data). Just because the representation is different between code and
data in those languages doesn't mean that you can't use data as code
(interpreter style) or generate code as data (compiler style). (I
won't bother boring you with the Python bytecode hacks or the parser
module, because I think those are not terribly practical.)

It's very elegant that Lisp makes the representations between code and
data so similar, but it looks like you know the mantra and not the
meaning if you can't apply that principle in other languages.

BTW... Even in Scheme, I have to use syntax-object->datum and it's
inverse to switch between the two. I suspect this has something to do
with the other context that must be associated with the data to turn it
into code. Probably at least the enclosing environment for lexical
scoping and the line numbers for debugging or exception handling. Does
Common Lisp maintain this information with it's macro expansions? If
so, you have a slight difference between code and data too. If not,
how do you get the line number when a problem happens in a nested
macro?

Moreover, if you really want to be pedantic about what "is" means in
"code is data", then you have to acknowledge that data is a superset of
code, since all code is obviously data, but there are plenty of types
of data that aren't used as code. Or are they? :)





Really? You're relying on an appeal to authority? Ok, I'm going to
start arguing by analogy then...

Analogies! My favorite! When a state trooper tells me not to drive my
Corvair over 55 that is argument from authority. When Ralph Nader tells
me....

Ok, I'll bite. What does it do?

There's been more on this elsewhere and I need to get some work done, so
i am afraid from now on it will have to be analogies and arguments from
authority, much faster.

I read through it, and it looks the
code you're passing to the macro does some things like calculating the
number of decimal digits and generating a 2 + a random number of that
many digits to find a divisor or something. It also looks like you
have some "string interpolation" to substitute names in your text, but
as a whole, I really don't have any clue what it's all about.

It looks like the macro itself is building some names on the fly and
defining methods (new specializations for multimethods?) with those
names.

So I'm sure I'm missing something, but there is almost certainly a
readable equivalent in Python (and even C). If you really want to
generate dynamic names for functions, you can do that in Python by
modifying the class or globals hash or whatever. A more standard way
might be to be have members in the base class.

There is even a multi-methods module in Python, but I've never used it.
I'd guess you could add to that dynamically too.





It's Python that doesn't allow you to decorate classes. (You can
decorate functions, but not classes...) I made this comment as a
criticism of Python since it seems like a non-orthogonal corner... It
would have fit in this case.

Decorators are pretty simple:

@foo
def bar(): pass

is equivalent to:

def bar(): pass
bar = foo(bar)

The callable foo can use whatever it wants to inspect, modify, or wrap
the function bar.

but:

@foo
class bar: pass

is not currently allowed, so you have to do

class bar: pass
bar = foo(bar)






That seems like a fine strategy. I fall more on the programmer effort
side. I want to write as little code as possible in a readable way
such that it meets the requirements.

You misunderstood. The Tilton Test is not indifferent to programmer
effort, it simply acknowledges that one cannot use it to compare
languages because one cannot control for programmer ability, which
varies more than language expressiveness. I am guessing (literally) that
we have a better chance of objective measures comparing two
community-polished code products passing the same functional tests.

I'm not a particularly good ambassador for Python. I know Python much
better than Lisp (Scheme), but I already think Scheme is a better
language. Macros are part of that. I'm mostly playing devil's
advocate here because I don't think your example really brings it home.
I've got limited time that I'm willing to invest in learning your
algebra system, but I haven't seen (understood, if you insist) anything
yet that couldn't be done readably in Python.

That is fine, this is a cultural exchange, merely disguised as a war to
keep the children amused. Again, I thought the environment suggestion
was promising, and reminded me that Python /does/ do some neat things in
re turning references into dictionary lookups such that datanames /can/
be at least generated dynamically.
If you want Common Lisp
to win in your language battle against the Pythonistas, you're going to
need to break out with something stronger. Then again, you're probably
pissing into the wind anyway.

A long, civil, technical thread contrasting Python and Lisp is not
exactly a loss to humanity. Too bad Ruby could not join us.

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
 
P

Paul Rubin

Raffael Cavallaro said:
Of course it does - Thats why we try ocaml and haskell etc. It's just
that we don't see the useful features of these languages as being
sufficiently useful to compensate for their lack of the ability to
easily do syntactic abstractions over a uniform syntax.

That sounds to me like a Blub programmer speaking.
For example, a common lisp with optional static typing on demand would
be strictly more expressive than common lisp. But, take say, haskell;
haskell's static typing is not optional (you can work around it, but
you have to go out of your way to do so); haskell's pure functional
semantics are not optional (again, workarounds possible to a limited
extent). This requires you to conceive your problem solution (i.e.,
program) within the framework of a particular paradigm. This lock-in
to a particular paradigm, however powerful, is what makes any such
language strictly less expressive than one with syntactic abstraction
over a uniform syntax.

Incorrect, I believe. The above is like saying Lisp's lack of
optional manual storage allocation and machine pointers makes Lisp
less powerful. It's in fact the absence of those features that lets
garbage collection work reliably. Reliable GC gets rid of a large and
important class of program errors and makes possible programming in a
style that relies on it. You can make languages more powerful by
removing features as well as by adding them. This is what Haskell
does, with its functional purity. Haskell's lazy evaluation semantics
pretty much depend on the purity.

See also SPJ's papers on composable memory transactions in Haskell:

http://research.microsoft.com/~simonpj/papers/stm/index.htm

These transactions rely on Haskell's pure functional semantics and if
I understand correctly, can't be implemented reliably without it. And
just like GC gets rid of a large class of pointer and storage
allocation errors, composable transactions in concurrent programs get
rid of lock-related errors, traditionally a huge source of problems in
real-world code.
 
G

greg

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

How did your macro know that the user's reverse function
needed a signed_value parameter in that particular case?
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.

You might be able to handle this using a general method
that searches the tree for a specified tag, e.g.

env.find_tag("signed_value")
 
J

John Thingstad

Incorrect, I believe. The above is like saying Lisp's lack of
optional manual storage allocation and machine pointers makes Lisp
less powerful. It's in fact the absence of those features that lets
garbage collection work reliably. Reliable GC gets rid of a large and
important class of program errors and makes possible programming in a
style that relies on it.

Truth with modifications. The foreign function library of most systems does
allow manual storage. If it didn't it would go into conniptions when
talking
to C where stored values are never moved.
You can make languages more powerful by
removing features as well as by adding them. This is what Haskell
does, with its functional purity. Haskell's lazy evaluation semantics
pretty much depend on the purity.

Some people like it some people don't.
(I should add I have never put serious effort into learning Haskell so my
opinion
should be taken with a grain of salt.)
Like many lispers I use multiple paradigms.
I have similar problems with SmallTalk by the way.
See also SPJ's papers on composable memory transactions in Haskell:

http://research.microsoft.com/~simonpj/papers/stm/index.htm

These transactions rely on Haskell's pure functional semantics and if
I understand correctly, can't be implemented reliably without it. And
just like GC gets rid of a large class of pointer and storage
allocation errors, composable transactions in concurrent programs get
rid of lock-related errors, traditionally a huge source of problems in
real-world code.

These days modern CPU's have multiple cores.
Parallelism is obviously easier to accomplish with a functional style
so I might take a closer look in the near future.
 
B

Bill Atkins

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

This sounds like the Turing argument, because that convenience makes
all the difference. Raw, unparsed text is nothing like trees of
symbols and objects when it comes to transforming or analyzing code.
 
B

Bill Atkins

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

Anaphoric macros are controversial in CL, too, for just that reason.
But there are a parallel set of macros that explicitly bind a variable
so you can get the convenience without the weirdness:

(bif (items (remove-if-not #'useful-p big-list-o-items))
(format t "~D items~%" (length items))
(format t "Nothing useful.~%"))

You can rewrite the expression in the grandparent as:

(aif (result (timeConsumingCalculation))
(use result)

or, more idiomatically:

(awhen (result (time-counsuming-calculation))
(use result))
 
K

Kaz Kylheku

Paul said:
Incorrect, I believe. The above is like saying Lisp's lack of
optional manual storage allocation and machine pointers makes Lisp
less powerful.

That is true. By itself, that feature makes Lisp less poweful for
real-world software dev, which is why we have implementation-defined
escape hatches for that sort of thing.
It's in fact the absence of those features that lets
garbage collection work reliably.

This is a bad analogy to the bondage-and-discipline of purely
functional languages.

The removal for the need for manual object lifetime computation does
not cause a whole class of useful programs to be rejected.

In fact, all previously correct programs continue to work as before,
and in addition, some hitherto incorrect programs become correct.
That's an increase in power: new programs are possible without losing
the old ones.

Wheas programs can't be made to conform to the pure functional paradigm
by adjusting the semantics of some API function. Programs which don't
conform have to be rejected,
Reliable GC gets rid of a large and
important class of program errors and makes possible programming in a
style that relies on it.

Right. GC gets rid of /program errors/. Pure functional programming
gets rid of /programs/.
You can make languages more powerful by removing features as well as by adding them.

Note that changing the storage liberation request from an imperative to
a hint isn't the removal of a feature. It's the /addition/ of a
feature. The new feature is that objects can still be reliably used
after the implementation was advised by the program that they are no
longer needed. Programs which do this are no longer buggy. Another new
feature is that programs can fail to advise the implementation that
some objects are no longer needed, without causing a leak, so these
programs are no longer buggy. The pool of non-buggy programs has
increased without anything being rejected.

Okay, that is not quite true, which brings me back to my very first
point. GC does (effectively) reject programs which do nasty things with
pointers. For instance, hiding pointers from being visible to GC.
However, such things can be made to coexist with GC. GC and non-GC
stuff /can/ and does, for pragmatic reasons, live in the same image.

Likewise, functional programming and imperative programming can also
coexist in the same image.

/Pure/ functional programming isn't about adding the feature of
functional programming. It's about eliminating other features which
are not functional programming.
 
J

Jon Harrop

Raffael said:
Because it doesn't require one to write a parser for each new syntax
for each new paradigm.

Why not use a single, extensible parser, e.g. camlp4?
It requires one to frame everything in functional terms or to jump
through the hoop of monads.

Can you give some example code demonstrating these hoops?
It isn't a question of "can't do in these other languages," it's a
matter of "can't do as easily in these other languages."

Yes, absolutely.
Look at kenny
tilton's cells. Its a dataflow paradigm built largely of macros. It
goes completely against the semantics of haskell - cells is all about
the eager evaluation of side effecting state mutation.

Perhaps that is because Cells is written in a language (Lisp) that forces
you to jump through hoops to get lazy evaluation?

The Cells project appears to deal with a graph (in the graph-theoretic
sense). There is nothing inherently side-effecting about that and the
problem that Cells is trying to solve seems to be fundamentally lazy:
update cells only when necessary.
Could you do it
in haskell? Yes, in the greenspun/turing-completeness sense, but not
nearly as easily as in common lisp, because the very paradigm - eager
evaluation combined with side effecting state mutation - goes against
the basic semantics of haskell.

Can you give some evidence to back that up, e.g. a program that solves a
problem like this in Haskell but is more convoluted that the Lisp?
You'd have to jump through extra hoops
to build a system whose very nature contradicts two of the semantic
foundations of haskell - laziness and absense of side effects.

What if eager impurity isn't the "very nature" of the problem but, rather,
is the very nature of Tilton's chosen solution?
Then there's the issue of the new syntax. Easy to build in lisp without
learning another language - lisp macros use lisp.

You can use Lisp macros to add some of the features found in modern FPLs.
Once you've done that, users of your modern Lisp variant must learn how to
use your macros. How is that better than learning a modern FPL directly?
What little I've seen
of caml4p looks like perlesque line noise. Ultimately I think that the
defaults of both haskell and ocaml - functional, static typing,
non-uniform syntax - are things I don't want as defaults, but as
options for later in the development of only certain pieces of code. I
don't want to be required to jump through the pure-functional,
must-use-monads-for-any-side-effects, static-type, non-uniform-syntax
hoops to express my ideas. It makes me less flexible in dealing with
individual parts of a program differently.

You can use tools like camlp4 to generate uniform syntax from OCaml's
syntax. You can also use the -dlambda to output OCaml's Lisp-like
intermediate representation.

Why do you think that uniform syntax like this Lisp:

(defun intersect (orig dir scene)
(labels ((aux (lam normal scene)
(let* ((center (sphere-center scene))
(lamt (ray-sphere orig
dir
center
(sphere-radius scene))))
(if (>= lamt lam)
(values lam normal)
(etypecase scene
(group
(dolist (kid (group-children scene))
(setf (values lam normal)
(aux lam normal kid)))
(values lam normal))
(sphere
(values lamt (unitise
(-v (+v orig (*v lamt dir))
center)))))))))
(aux infinity zero scene)))

is better than syntax with conventional mathematical grammar, like this
equivalent OCaml:

let rec intersect orig dir (lambda, _ as hit) (center, radius, scene) =
let lambda' = ray_sphere orig dir center radius in
if lambda' >= lambda then hit else match scene with
| Sphere -> lambda', unitise (orig +| lambda' *| dir -| center)
| Group scenes -> List.fold_left (intersect orig dir) hit scenes

From my point of view, people started with Lisp and used its macros to add
new syntax to the language and they got Haskell and OCaml. After all,
Haskell and OCaml are more popular that any given Lisp variant with similar
features (e.g. pattern matching), AFAIK.
 
R

Raffael Cavallaro

What if eager impurity isn't the "very nature" of the problem but, rather,
is the very nature of Tilton's chosen solution?

That's the whole point which you keep missing - that a programming
language is expressive precisely to the extent that it allows you to
express the solution in the *programmer's* chosen form, not the
paradigm imposed by the language.

You look down your nose at cells, but if that's the way kenny conceived
of the problem - as a graph of changing state, why should he be forced
to reconceptualize it according to someone else's notion of programming
correctness (be that pure functional or any other paradigm)?

By asking this question you've implicitly admitted that to solve it *as
he thought of it* in a pure functional language would require
reconceptualizing it (i.e., the aforementioned "jumping through
hoops"). We don't want to reconceptualize everything according to a
particular paradigm, we want the flexibility to write the solution to
the problem in the terms we think and talk about it, not the
procrustean bed of pure functional semantics.
 

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