merits of Lisp vs Python

R

Ravi Teja

Jean-Paul Calderone said:
Timofei Shatrov wrote:

[snip]

Of course, doctest is hardly the ultimate testing solution. But it does
an admirable job for many cases where you don't need to setup elaborate
tests.
It's not surprising that no one uses this stuff for serious work.

I have seen enough libraries that use doctest. Zope, Twisted and Paste
are some of the popular Python projects in that use it. Epydoc supports
it as well.

Just as a factual correction, Twisted does not use doctests.

Jean-Paul

You are right. I did a quick search on my Python Lib folder for that
post to list some well known projects. Twisted does have some doctests
in there but does not use them for actual testing (apparently, only to
check for doctest support).

Ravi Teja.
 
X

xscottg

Paul said:
Similarly in C programs, don't do

*random = 0;

Avoiding that is easier said than done. C programs suffer endless
bugs of that type.

I see your point. Since that kind of error is less likely in Lisp, and
we want less bugs in our device drivers, device drivers should be
written in Lisp. Only using low level peeks and pokes where absolutely
necessary. It makes good sense, and I'm glad you talked me into it.
:)

Cheers,
-Scott
 
B

Bill Atkins

This is not a response to any particular post, but rather to the
general argument that macros are not as useful as we Lispers claim.

Here is a fairly complete GUI RSS reader in 90 lines of Lisp (the GUI
code itself is 90 lines, but it makes use of some RSS reading/writing
code I had laying around and two public CL libraries: s-xml and
trivial-http). The code employs a handy macro called DEFINE-INTERFACE
that LispWorks provides with their CAPI toolkit. I hope this will be
useful as an illustration of what macros (and Lisp in general) can do,
and as an example of what a moderately practical CL application looks
like.

The application presents a list of feeds and allows the user to add
new feeds or to delete existing feeds. The feeds are presented as a
tree, with each channel acting as a parent to several items. The user
can refresh all of the feeds with the "Refresh All" button.
Double-clicking on any item will display its description field (as in
the screenshot). Each channel shows the number of unread articles and
the articles are arranged so that the unseen articles appear before
the articles that have already been read.

Important things to note:

1) the DEFINE-INTERFACE syntax closely resembles the syntax of the
standard DEFCLASS macro, so newcomers have a basic idea of what
DEFINE-INTERFACE is going to do

2) the expansion ( http://galoot.org/~bill/code/rss-reader/expanded.lisp )
of just the DEFINE-INTERFACE is quite involved, a testament to
the amount of work the macro saves

3) much of the GUI can be specified declaratively because of the
DEFINE-INTERFACE syntactic abstraction, and this declarativeness
makes it very easy for maintainers to understand what's going on

4) even without knowing the implementation of DEFINE-INTERFACE and
even without prior experience with it, one can make good guesses
about what it does

5) the GUI code is stunningly concise

Here is the GUI code alone:

< http://galoot.org/~bill/code/rss-reader/rss-gui.lisp >

Here is a screenshot of the application in development:

< http://galoot.org/~bill/code/rss-reader/development.png >

and two screenshots of the application in use

< http://galoot.org/~bill/code/rss-reader/in action.png >
< http://galoot.org/~bill/code/rss-reader/in action 2.png >

Here are the support files:

< http://galoot.org/~bill/code/rss-reader/ >

Here is an OS X universal binary (run at your own risk; I've only
done very basic testing):

< http://galoot.org/~bill/code/rss-reader/BarebonesRSS.app.tar.gz >

Enjoy.
 
B

Bruno Desthuilliers

Paul Rubin a écrit :
I'm not aware of any special features in Haskell for that purpose, or
in Scheme until maybe with the more recent versions. I thought the
main feature needed for functional programming besides first-class
functions was guaranteed tail call optimization.

Strictly speaking, only first-class functions are required, and
tail-recursion optimisation is only an implentation detail. Now it's
obvious that when it comes to real-life-size programs, this is a *very*
important detail !-)
 
?

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

Paul said:
GC also gets rid of programs. There are programs you can write in C
but not in Lisp, like device drivers that poke specific machine
addresses.

You are talking about an ANSI Common Lisp implementation.
But nothing stops a vendor to deliver its CL with libs that support
the writing of device drivers.
When Naughty Dog programmed games for the Play Station 2 the used
Common Lisp to develop a Lisp called "GOAL" (Game Oriented Assembly
Lisp) that was specialized for writing PS2 games.


André
--
 
B

Bruno Desthuilliers

Mathias Panzenboeck a écrit :
You mean like function pointers in C and C++?

Absolutely not. Python's functions are normal Python objects, instances
of the (builtin) class 'function'. FWIW, any object implementing the
__call__ method can behave as a function.

Python functions can take functions as arguments, and return functions -
this is how 'decorators' work.
I think this should be possible in assembler, too.
I thought functional languages have to be declarative?

For what definition of 'declarative' ?
The boost C++ library has even lambdas!

So does Python - even if in a restricted way.
 
B

Bruno Desthuilliers

Kaz Kylheku a écrit :
False. Common Lisp can be made to support SQL syntax.
And Python can be made to support something really close to it, cf
SQLAlchemy. And Python has list comprehensions, which are mostly on the
declarative side (ie : "what, not how"):

[x for x in range(100) if x % 2 == 0]

Etc, etc, etc...
 
R

Rob Warnock

+---------------
| Paul Rubin wrote:
| > [...] There are programs you can write in C but not in Lisp,
| > like device drivers that poke specific machine addresses.
|
| I should assume you meant Common Lisp, but there isn't really any
| reason you couldn't
| (poke destination (peek source))
| in some version of Lisp that was meant for writing device drivers
| (perhaps under a Lisp machine or something).
+---------------

I do this kind of thing *every day* in CMUCL!! It's my primary
user-mode hardware debugging tool. Here's a typical utility function:

;;; Used for things like polling for a "done" flag.
;;; BUG: Assumes contents of ADDR will change within fixnum polls.
(defun spin-until-change (addr)
(declare (optimize (speed 3) (debug 0) (safety 0)))
(loop with v0 of-type (unsigned-byte 32) = (r32 addr)
with counts fixnum = 0
while (= v0 (r32 addr))
do (setf counts (the fixnum (1+ counts)))
finally (return counts)))

+---------------
| SIOD actually has (%%% memref address) for peek.
+---------------

CMUCL has SYSTEM:SAP-REF-{8,16,32} and SETFs of same. The R32
above is just my convenience wrapper around SYSTEM:SAP-REF-32:

(declaim (inline r32))

(defun r32 (addr)
(declare (optimize (speed 3) (debug 0) (safety 0)))
(system:sap-ref-32 (system:int-sap addr) 0))

(defun w32 (addr &rest values)
(declare (optimize (speed 3) (debug 0) (safety 0)))
(loop for i fixnum from 0 by 4
and v of-type (unsigned-byte 32) in values
do (setf (system:sap-ref-32 (system:int-sap addr) i) v))
(values))

Most other Common Lisp implementations surely have something similar.


-Rob
 
P

Paul Rubin

Bruno Desthuilliers said:
Strictly speaking, only first-class functions are required, and
tail-recursion optimisation is only an implentation detail. Now it's
obvious that when it comes to real-life-size programs, this is a
*very* important detail !-)

I don't buy this. One of my favorite quotes about specifications
(http://www.sgi.com/tech/stl/drdobbs-interview.html):

Let's take an example. Consider an abstract data type stack. It's not
enough to have Push and Pop connected with the axiom wherein you push
something onto the stack and after you pop the stack you get the same
thing back. It is of paramount importance that pushing the stack is a
constant time operation regardless of the size of the stack. If I
implement the stack so that every time I push it becomes slower and
slower, no one will want to use this stack.

We need to separate the implementation from the interface but not at
the cost of totally ignoring complexity. Complexity has to be and is a
part of the unwritten contract between the module and its user. The
reason for introducing the notion of abstract data types was to allow
interchangeable software modules. You cannot have interchangeable
modules unless these modules share similar complexity behavior. If I
replace one module with another module with the same functional
behavior but with different complexity tradeoffs, the user of this
code will be unpleasantly surprised. I could tell him anything I like
about data abstraction, and he still would not want to use the
code. Complexity assertions have to be part of the interface.
 
J

jayessay

Paul Rubin said:
That breaks the reliability of GC. I'd say you're no longer writing
in Lisp if you use something like that.

Please note: GC is not part of CL's definition. It is likely not part
of any Lisp's definition (for reasons that should be obvious), and for
the same reasons likely not part of any language's definition. So,
your point here is actually a category error...


/Jon
 
G

greg

Bill said:
This is not a response to any particular post, but rather to the
general argument that macros are not as useful as we Lispers claim.

Here is a fairly complete GUI RSS reader in 90 lines of Lisp

For comparison, here's how something with a similar
API might be used from Python.

class RSSInterface(Interface):

def __init__(self):
Interface.__init__(self,
panes = [
TextInput('url_pane',
title = 'Feed URL:',
callback = 'add_feed',
callback_type = 'interface'),
PushButton('add',
text = 'Add Feed',
callback = 'add_feed',
callback_type = 'interface'),
PushButton('refresh',
text = 'Refresh All',
callback = 'refresh_feeds',
callback_type = 'interface'),
PushButton('delete',
text = 'Delete Feed',
callback = 'delete_feed',
callback_type = 'interface'),
TreeView('tree',
visible_min_width = ('character', 84),
visible_min_height = ('character', 40),
action_callback = 'browse_item',
callback_type = 'item_interface',
expandp_function = lambda: True, # not sure how best to translate
children_function = 'tree_item_children',
print_function = 'tree_item_string')
],
layouts = [
ColumnLayout('main', ('top', 'tree')),
RowLayout('top', ('url_pane', 'add', 'refresh', 'delete'))
],
title = 'Barebones RSS Reader v1.0')
self.channels = [
parse_rss_from_url(url) for url in [
'http://planet.lisp.org/rss20.xml',
'http://feeds.theonion.com/theonion/daily']]

def add_feed(self):
...

def delete_feed(self):
...

# etc.
 
J

Jon Harrop

Rob said:
Once you can do the above then you can phrase programs entirely in
terms of composition of functions, which is what functional programming
is about.

There are many aspects to functional programming. Some languages (like Lisp
and Python) are very impure and hardly encourage functional programming.
Other languages (like OCaml, SML, F# and Scheme) are impure but make
functional programming easy (e.g. higher-order functions, currying,
continuation passing style are all concise and statically checked). Some
languages (like Haskell) are purely functional, so everything must be
immutable.
Getting good performance though is problematic without being able to
evaluate parts at compile time. This is why almost all functional
languages provide that feature in some form.

Actually the languages that don't provide compile-time execution (OCaml, SML
and F#) are typically much faster than those that do (Lisp, Scheme, Dylan).
 
A

Anders J. Munch

jayessay said:
> Please note: GC is not part of CL's definition. It is likely not part
of any Lisp's definition (for reasons that should be obvious), and for
the same reasons likely not part of any language's definition.

Really? So how do you write a portable program in CL, that is to run
for unbounded lengths of time?

- Anders
 
R

Rob Thorpe

Anders said:
Really? So how do you write a portable program in CL, that is to run
for unbounded lengths of time?

You can't.

The thing about the spec not defining GC is almost a bit of humour.
No-one would use an implementation with no GC.

The issue with specifying it is: How would you do it? The memory used
by a program is an aspect of the language implementation and the system
the program is running on, so how can it be defined in a useful way?

You could say for example "Storage allocated for an object is released
when the object is no longer visible and memory usage is high". But
how do you define how high memory usage should be? You could say when
memory is almost exhausted, even that is difficult to define. Then you
could have the situation where someone creates a lisp compiler than
emits FPGA netlists, how do you check something like this?

None of this would be in the spirit of the spec, which doesn't even
define memory AFAIK. The spec deals entirely in matters of the
language and it's appearance to the programmer. For what it's worth I
think the C spec is the same, and says nothing about actual memory
usage.
 
J

josephoswaldgg

Anders said:
Really? So how do you write a portable program in CL, that is to run
for unbounded lengths of time?

- Anders

Write it, and depend on the implementation to do its job well, and
complain to the implementors if it doesn't.

How do you write a portable program in C that is to run for unbounded
lengths of time? Even if you religiously call free() on every
malloc()'d block, you could eventually fragment memory enough that the
allocator might eventually fail. You can exceed the VM capacity. The OS
could decide to crash periodically. The power supply could fail, the
sun could progress to the red giant phase...

The real answer is that these are not issues for the language
definition, but for the implementor.
 
P

Pascal Bourguignon

Rob Thorpe said:
You can't.

You can. Just use reversible operations.

Or use pre-allocated objects. Yes, that means that you implement your
own memory management or garbage collector, but this would be portable.
 
R

Rob Thorpe

Pascal said:
You can. Just use reversible operations.

Or use pre-allocated objects. Yes, that means that you implement your
own memory management or garbage collector, but this would be portable.

I don't think there is any gaurantee in the spec that says that even
doing nothing at all doesn't allocate more memory. AFAIK a conforming
implementation could leak a fixed amount of memory per second.
 
A

Anders J. Munch

Rob said:
>
> You can't.
>
> The thing about the spec not defining GC is almost a bit of humour.
> No-one would use an implementation with no GC.
>
> The issue with specifying it is: How would you do it? The memory
> used by a program is an aspect of the language implementation and
> the system the program is running on, so how can it be defined in a
> useful way?

Let u(t) be the actual memory used by the program at time t.
Let r(t) be the size of reachable memory at time t.

Require that u(t) is a member of O(t -> max{t'<=t: r(t')})

There. That wasn't so hard, was it?

- Anders
 
R

Rob Thorpe

Anders said:
Let u(t) be the actual memory used by the program at time t.
Let r(t) be the size of reachable memory at time t.

Require that u(t) is a member of O(t -> max{t'<=t: r(t')})

There. That wasn't so hard, was it?

That's quite a clever definition actually.
But, let's say I have a lisp machine. It has an unusual architecture,
it's made entirely of SRAM cells of ~9bits. Sometimes these cells are
used as storage, sometimes their contents represent logic circuits and
the routing between them is configured to form them into a processor.
Please tell me what reachable memory is ;). (The above processor is
not science fiction, it could easily be done with FPGAs)

I suppose a solution would be:-

If the lisp implementation runs on a fixed processor that has separate
storage then ...

Let u(t) be the actual memory used by the program at time t.
Let r(t) be the size of reachable memory at time t.
....
 

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
473,780
Messages
2,569,611
Members
45,276
Latest member
Sawatmakal

Latest Threads

Top