Program compression

153 views
Skip to first unread message

Your Name

unread,
Jun 9, 2008, 12:55:02 PM6/9/08
to
I've learned and used quite a few programming languages, accross just
about all of the "paradigms." I'm sure there are many in this group who
have learned and use more.

I'd like to share what I've found common to all of them. First, there
is some syntax that must be learned. Then there is a period of becoming
familiar with syntax and sementics, as one learns a crucial thing: "how
to get the job done" with this language. Certainly, too many language
learners reach this point and plateau in their learning progress.

But any serious programmer of a given language will undoubtedly take the
next step, which is the one that I find the most interesting. Only
after about 25+ years of programming did this perticular process, common
to (probably) all languages, become clear.

The process, for lack of a better term, is "compression." The first
form of this that most of us programmers encounter is subroutines.

Instead of this:


do_something_1 with X
do_something_2 with X
do_something_3 with X

do_something_1 with Y
do_something_2 with Y
do_something_3 with Y


we compress to this:


do_some_things(argument)
{
do_something_1 with argument
do_something_2 with argument
do_something_3 with argument
}

do_some_things with X
do_some_things with Y


This refinement makes obvious sense in many ways, including
maintainability, readability, code space reduction, etc... But I
realize now that this step is the real essence of programming. In every
useful language I can think of, this compression is really the central
feature.

The whole concept of "object oriented" programming is nothing more than
this. Code and data common to various objects are moved to a "parent"
or "base" class. Objects can either derive from other objects ("X is a
Y"), or contain other objects ("X has a Y").

Even scripting languages like HTML have incorporated this concept. When
this inevitably became too cumbersome:


<font family="Arial" color="#000000" size="3">Hello</font>
<font family="Arial" color="#000000" size="3">World</font>


the common elements were separated:


<style type="text/css">
h2
{
font-family: Arial;
font-size: 12pt;
color: #000000;
}
</style>

<h2>Hello</h2>
<h2>World</h2>


I would wager that most programmers, especially in the beginner to
intermediate realm, don't really understand why this type of design is
desireable, but just find that it feels right. Maybe the short term
payoff of simply having to type less is the incentive.

But the reason is deeper. A very simple algorithm for compressing data
is run-length-encoding. The following data, possibly part of a
bitmapped image:

05 05 05 05 05 05 05 02 02 02 17 17 17 17 17

Can be run-length-encoded to:

07 05 03 02 05 17

The reward, at first, is just a smaller file. But at a deeper level,
the second version could be considered "better," in that it is more than
just a mindless sequence of bytes. Some meaning is now attached to the
content. "Seven fives, followed by three twos, followed by five
seventeens" is much less mind numbing than "five, five, five, five,
five, five, five, two, two..."

It has been argued that compression is actually equivalent to
intelligence. This makes sense at a surface level. Instead of solving
a problem with a long sequence of repetitious actions, understanding the
problem allows us to break it into more manageable pieces. The better
our understanding, the more compression we can achieve, and the more
likely our resulting algorithm will be suited to solving similar
problems in the future.

This was quite a revelation for me, and it shed much light on writing
"good" code. It also made clear why I find some languages much more
useful than others. The more power a language gives me to compress my
algorithm -- both code and data, as well as in space and executuion time
-- the more I like it. The true measure of this is not the number of
bytes required by the source code, although this surely has some
correlation.

This has given me a great deal of direction in thinking about creating
languages.

Richard Heathfield

unread,
Jun 9, 2008, 2:46:12 PM6/9/08
to
Your Name said:

<snip>

> But any serious programmer of a given language will undoubtedly take the
> next step, which is the one that I find the most interesting. Only
> after about 25+ years of programming did this perticular process, common
> to (probably) all languages, become clear.
>
> The process, for lack of a better term, is "compression."

Or abstraction, or functional decomposition...

> The first
> form of this that most of us programmers encounter is subroutines.
>
> Instead of this:
>
>
> do_something_1 with X
> do_something_2 with X
> do_something_3 with X
>
> do_something_1 with Y
> do_something_2 with Y
> do_something_3 with Y
>
>
> we compress to this:
>
>
> do_some_things(argument)
> {
> do_something_1 with argument
> do_something_2 with argument
> do_something_3 with argument
> }
>
> do_some_things with X
> do_some_things with Y

No, we compress to this:

do_some_things(obj, min, max)
{
while(min <= max)
{
do_something with min++, obj
}
}

do_some_things with X, 1, 3
do_some_things with Y, 1, 3

or even:

object = { X, Y }
for foo = each object
{
do_some_things with foo, 1, 3
}

> Maybe the short term
> payoff of simply having to type less is the incentive.

Don't forget elegance. It's impossible to define, but a good programmer
knows it when he/she/it sees it.

> But the reason is deeper. A very simple algorithm for compressing data
> is run-length-encoding. The following data, possibly part of a
> bitmapped image:
>
> 05 05 05 05 05 05 05 02 02 02 17 17 17 17 17
>
> Can be run-length-encoded to:
>
> 07 05 03 02 05 17
>
> The reward, at first, is just a smaller file. But at a deeper level,
> the second version could be considered "better," in that it is more than
> just a mindless sequence of bytes.

There are better algorithms than RLE. :-)

> It has been argued that compression is actually equivalent to
> intelligence. This makes sense at a surface level. Instead of solving
> a problem with a long sequence of repetitious actions, understanding the
> problem allows us to break it into more manageable pieces. The better
> our understanding, the more compression we can achieve, and the more
> likely our resulting algorithm will be suited to solving similar
> problems in the future.

This is sometimes expressed in the form "theories destroy facts". If you
know the equation, you don't need the data!

> This was quite a revelation for me, and it shed much light on writing
> "good" code. It also made clear why I find some languages much more
> useful than others. The more power a language gives me to compress my
> algorithm -- both code and data, as well as in space and executuion time
> -- the more I like it. The true measure of this is not the number of
> bytes required by the source code, although this surely has some
> correlation.

Expressive power matters a lot, and you are right to highlight its
importance.

> This has given me a great deal of direction in thinking about creating
> languages.

Incidentally, it has also given this newsgroup the possibility of entering
into a worthwhile discussion that isn't based on a lame newbie question.
Nice one.

--
Richard Heathfield <http://www.cpax.org.uk>
Email: -http://www. +rjh@
Google users: <http://www.cpax.org.uk/prg/writings/googly.php>
"Usenet is a strange place" - dmr 29 July 1999

Robert Maas, http://tinyurl.com/uh3t

unread,
Jun 9, 2008, 4:20:47 PM6/9/08
to
> From: Your Name <n...@none.none>
(when learning a new programming language, your very first, or yet
another different from what you previously knew/used:)

> First, there is some syntax that must be learned.
> Then there is a period of becoming familiar with syntax and
> sementics, as one learns a crucial thing: "how to get the job
> done" with this language.

I agree completely. In fact these are the start of my course
outline for teaching computer programming to absolute beginners:
<http://www.rawbw.com/~rem/HelloPlus/hellos.html#s4outl>
Notice how lesson 1 is just the syntax used to express values, and
then lesson 2 starts the semantics of using some of those values as
programs (i.e. passing them to EVAL). So lesson 1 uses with a
read-describe-print loop, then lesson 2 adds a call to EVAL in the
middle.

(What happens next:)

> The process, for lack of a better term, is "compression."

This is a subset of "refactoring". In fact I strongly advise that
beginners (and even experts much of the time) write single lines of
code and get each single line of code working before moving onto
the next line of code, and only after getting the "business logic"
(the data transformations) working for some function *then*
refactor those lines of code into a self-contained function
definition. I suppose "agile programming" is the most common
buzzword expressing something like this methodology. Instead of
treating refactoring as a pain to be avoided by correct design in
the first place, treat refactoring as a dominant part of the
software development process. You *always* write easily debuggable
code, and then *after* you have it working you *always* refactor it
to be better for long-term use, with the tradeoff that it's now
more difficult to debug, but since debugging at such a low level of
code has been mostly finished this isn't a problem.

Notice how my lesson plan, after the really basic semantics of individual
lines of code, proceeds to teach refactoring in several different ways:
* Lesson 3: Putting micro-programs in sequence to do multi-step D/P
(data processing), and building such sequences into named
functions
* Lesson 4: Refactoring syntax: Getting rid of most uses of GO in
PROG.
* Lesson 5: Refactoring algorithms: Devising data structures that
make D/P much more efficient than brute-force processing of flat
data sequences.
After that point, my beginning lessons don't discuss additional
ways of refactoring, but I think we would agree that further
refactoring is *sometimes* beneficial:
- Using OOP.
- Using macros to allow variations on the usual syntax for calling functions.
- Defining a whole new syntax for specialized problem domains,
including a parser for such syntax.
Almost any software project can benefit from bundling lines of code
into named functions (and sometimes anonymous functions), and also
refactoring syntax and algorithms/dataStructures. But whether a
particular project can benefit from those three additional
refactorings depends on the project. Perhaps my course outline for
an absolute beginner's course in how to write software is
sufficient, and OOP/macros/newSyntaxParsers should be a second
course for people who have gotten at least several months
experience putting the lessons of the first course to practice? If
you like my absolute-beginner's course outline, would you be
willing to work with me to develop a similarily organized outline
for a second course that covers all the topics in your fine essay
and my three bullet points just above?

> The more power a language gives me to compress my algorithm --
> both code and data, as well as in space and executuion time -- the
> more I like it.

Hopefully you accept that Lisp (specifically Common Lisp) is the
best language in this sense? Common Lisp supports, in a
user/applicationProgrammer-friendly way, the complete process of
(agile) programming from immediately writing and testing single
lines of code all the way through all the refactorings needed to
achieve an optimal software project. Java with BeanShell is a
distant second, because the semantics of individual statements
given to BeanShell are significantly different from the semantics
of exactly the (syntactically) same statements when they appear in
proper method definitions which appear within a proper class
definition which has been compiled and then loaded.

> This has given me a great deal of direction in thinking about
> creating languages.

There's no need to create another general-purpose programming
language. Common Lisp already exists and works just fine. All you
might need to do is create domain-specific languages, either as
mere sets of macros within the general s-expression syntax, or as
explicitly parsed new syntaxes feeding into Lisp, or as GUI-based
no-syntax pure-semantics editors generating tables that feed into Lisp.

Nick Keighley

unread,
Jun 10, 2008, 6:22:40 AM6/10/08
to
On 9 Jun, 17:55, Your Name <n...@none.none> wrote:

<snip>

> But any serious programmer of a given language will undoubtedly take the
> next step, which is the one that I find the most interesting.  Only
> after about 25+ years of programming did this perticular process, common
> to (probably) all languages, become clear.

independent of language.


> The process, for lack of a better term, is "compression."  The first
> form of this that most of us programmers encounter is subroutines.

<snip example of common code>

This is the "Refactor Mercilessly" of the Agile crowd.


> This refinement makes obvious sense in many ways, including
> maintainability, readability, code space reduction, etc...  But I
> realize now that this step is the real essence of programming.  In every
> useful language I can think of, this compression is really the central
> feature.
>
> The whole concept of "object oriented" programming is nothing more than
> this.  Code and data common to various objects are moved to a "parent"
> or "base" class.  Objects can either derive from other objects ("X is a
> Y"), or contain other objects ("X has a Y").

now here I disagree. Thinking about OO like this tends at best to lead
to really deep inheritance trees and at worst LSP violations. "yes I
know
a Widget isn't really a ClockWorkEngine but I used inheritance to
share
the code".

OO is about identifying abstractions. Look up the Open Closed
Principle
for a better motivator for OO.

Read The Patterns Book.

<snip>

--
Nick Keighley

Programming should never be boring, because anything
mundane and repetitive should be done by the computer.
~Alan Turing


I'd rather write programs to write programs than write programs

Richard Heathfield

unread,
Jun 10, 2008, 6:55:57 AM6/10/08
to
Nick Keighley said:

<snip>


>
> OO is about identifying abstractions. Look up the Open Closed
> Principle
> for a better motivator for OO.
>
> Read The Patterns Book.

That's unconstitutional in the USA, because it's cruel and unusual
punishment. It remains legal in the UK, though.

Your Name

unread,
Jun 10, 2008, 2:38:20 PM6/10/08
to
Nick Keighley <nick_keigh...@hotmail.com> wrote:

> now here I disagree. Thinking about OO like this tends at best to lead
> to really deep inheritance trees and at worst LSP violations. "yes I
> know
> a Widget isn't really a ClockWorkEngine but I used inheritance to
> share
> the code".


At the risk of a religious debate, I have to respond that I don't really
find OO very useful. One reason for this is probably exactly what you
just said. I see that tendency as a failure of OO.

Initially, it is nice to be able to say "a square is a shape" and "a
shape has an area." But the more complex the project, the more
inheritance and information hiding become nothing more than a burden, in
my experience.

They may be necessary evils in some environments. The open/closed
principle reduces modifications to the "base" set of code -- whether
this is a set of classes, a library of functions, or something else. It
encourages code to become set in stone. Unfortunately, especially on a
large project, I find that no matter how we try, it is impossible to
foresee the entire scope of what needs to be designed. So the design is
constantly changing, and the idea of some set of code that we write in
the early stages surviving without needing vast rewrites for both
functionality and efficiency is delusional.

Our current project is enormous. It involves half a dozen separate
applications, running on completely different platforms (some x86, some
embedded platforms, some purely virtual). The code ranges from very
high-level jobs, like 3D rendering, all the way down to nuts-and-bolts
tasks like device drivers. It even includes developing an operating
system on the embedded side.

I can honestly say that OO has really added nothing at any stage of that
chain, from the lowest level to the highest. We use C++ for two
reasons:

1) The development tools and libraries are the most mature for C++ and
this is essential.

2) The OS, drivers, and application code in the embedded firmware is all
C and we have no choice in that, unless we want to develop a compiler
for some other language, or write directly in assembly language. Since
C and C++ are largely similar, parts of the C code that we want to share
with the other applications can be used directly.

But if not for these two restrictions, in hindsight, I would not have
chosen C++ or any other OO language.

I've wandered far enough off-topic from the original post.

John W. Krahn

unread,
Jun 10, 2008, 2:57:36 PM6/10/08
to
Richard Heathfield wrote:
> Nick Keighley said:
>
> <snip>
>> OO is about identifying abstractions. Look up the Open Closed
>> Principle
>> for a better motivator for OO.
>>
>> Read The Patterns Book.
>
> That's unconstitutional in the USA, because it's cruel and unusual
> punishment. It remains legal in the UK, though.

I understand that the current US VP has changed the definition of "cruel
and unusual" so that it's not unconstitutional any more. Fortunately I
live in neither the US nor the UK. :-)


John
--
Perl isn't a toolbox, but a small machine shop where you
can special-order certain sorts of tools at low cost and
in short order. -- Larry Wall

Steve O'Hara-Smith

unread,
Jun 10, 2008, 4:00:10 PM6/10/08
to
On Tue, 10 Jun 2008 18:38:20 GMT
Your Name <no...@none.none> wrote:

> They may be necessary evils in some environments. The open/closed
> principle reduces modifications to the "base" set of code -- whether
> this is a set of classes, a library of functions, or something else. It
> encourages code to become set in stone. Unfortunately, especially on a
> large project, I find that no matter how we try, it is impossible to
> foresee the entire scope of what needs to be designed. So the design is
> constantly changing, and the idea of some set of code that we write in
> the early stages surviving without needing vast rewrites for both
> functionality and efficiency is delusional.

I have seen OO work wonderfully well with core classes surviving
seven years of product development with them at the heart unchanged in API
(the internals got a severe optimisation at one point). They were used in
applications never even suspected at the start and the core aspect
mechanism held up under the strain superbly.

I have also seen OO fail horribly yielding nothing but awkward
constructs that impede design and make life difficult exploding into a
myriad of similar but vitally different classes requiring constant tweaking
with resultant adjustments all over the codebase.

The key difference as far as I can tell has to do with a rigorous
attack on assumptions in the core design eliminating everything except the
core concepts the base classes are intended to provide and then layering
with care. That and the craft to design a truly useful set of simple
abstractions expressing the immutable aspects of the problem domain.

The idea is not delusional, I have seen it work on more than one
occasion. It is however a difficult art and almost certainly not suited (or
possible) in all problem domains. When it works it's like putting a magic
wand in the hands of the developers, when it fails it's like casing their
hands in two kilos of half set epoxy resin.

--
C:>WIN | Directable Mirror Arrays
The computer obeys and wins. | A better way to focus the sun
You lose and Bill collects. | licences available see
| http://www.sohara.org/

Your Name

unread,
Jun 10, 2008, 7:49:41 PM6/10/08
to
Robert Maas wrote:

> Hopefully you accept that Lisp (specifically Common Lisp) is the
> best language in this sense? Common Lisp supports, in a
> user/applicationProgrammer-friendly way, the complete process of
> (agile) programming from immediately writing and testing single
> lines of code all the way through all the refactorings needed to
> achieve an optimal software project.

I have to admit that I haven't used Lisp since college. At the time, I
found it interesting, and I seem to recall that it was well suited for
things like AI development. I also remember it being difficult to write
readable code. Professionally, I'm somewhat handcuffed to C and C++ for
reasons I mentioned earlier. It's also hard to find bright and
motivated employees who are fluent in Lisp. But I will make a point of
revisiting it.

I'd just like a language where this idea of compression, or refactoring,
is the central principle around which the language is built. Any
language that supports subroutines offers a mechanism for this, but I
feel the concept could be taken further. It's all very nebulous at the
moment, but I feel that somehow the answer lies in pointers.

At its heart, a subroutine is nothing more than a pointer, in any
language. A compiler takes the code inside the routine and stuffs it
into memory somewhere. To call that function, you just dereference the
pointer to "jump" to that code.

An object in an OO language is accomplished using a pointer. Each class
has a function table, and each object has a pointer to that function
table. Here, the compression is happening at the compiler level. Since
every object of a given class has the same code, it can all be moved to
a common place, then each object need only store a pointer to it.
Further, code in a base class need only exist there, with each child
class pointing to that table.

But these are hard-coded abstractions built into compilers. C++ or Java
tell you, "This is how inheritance works... This is the order in which
constructors are called..." Somehow, I'd like for the language itself
to make the mechanism directly accessible to the programmer. If his or
her programming style then naturally tends to evolve into an OO sort of
structure, wonderful. If not, then maybe a sort of table-driven type of
"engine" architecture would emerge. That just happens to be what I
generally find most powerful.

I suppose you could just take a huge step backwards and write pure
assembly language. Then all you really have is code, data, and
pointers. You're free to use them however you like. But I strongly
believe there is a way to still offer this freedom, while also offering
a great deal of convenience, readability, and maintainability by way of
a high-level language.

thomas...@gmx.at

unread,
Jun 12, 2008, 6:30:40 AM6/12/08
to

If you want to invent your own abstraction mechanisms you
might be interested in Seed7. There are several constructs
where the syntax and semantic can be defined in Seed7:
- Statements and operators (while, for, +, rem, mdiv, ... )
- Abstract data types (array, hash, bitset, ... )
- Declaration constructs
The limits of what can be defined by a user are much wider
in Seed7.

Greetings Thomas Mertes

Seed7 Homepage: http://seed7.sourceforge.net
Seed7 - The extensible programming language: User defined statements
and operators, abstract data types, templates without special
syntax, OO with interfaces and multiple dispatch, statically typed,
interpreted or compiled, portable, runs under linux/unix/windows.

Jon Harrop

unread,
Jun 17, 2008, 6:52:33 AM6/17/08
to
Your Name wrote:
> This has given me a great deal of direction in thinking about creating
> languages.

I agree.

--
Dr Jon D Harrop, Flying Frog Consultancy
http://www.ffconsultancy.com/products/?u

Jon Harrop

unread,
Jun 17, 2008, 7:28:00 AM6/17/08
to
Robert Maas, http://tinyurl.com/uh3t wrote:
>> The more power a language gives me to compress my algorithm --
>> both code and data, as well as in space and executuion time -- the
>> more I like it.
>
> Hopefully you accept that Lisp (specifically Common Lisp) is the
> best language in this sense?

If that were true Lisp would be concise but Lisp is actually extremely
verbose.

>> This has given me a great deal of direction in thinking about
>> creating languages.
>
> There's no need to create another general-purpose programming
> language. Common Lisp already exists and works just fine. All you
> might need to do is create domain-specific languages, either as
> mere sets of macros within the general s-expression syntax, or as
> explicitly parsed new syntaxes feeding into Lisp, or as GUI-based
> no-syntax pure-semantics editors generating tables that feed into Lisp.

Consider the following questions:

1. Why has Lisp always remained so unpopular when, as you say, it is so
extensible?

2. Why have the implementors of successful modern languages that were
originally built upon Lisp gone to great lengths to completely remove Lisp
from their implementations?

I studied Lisp for some time before realising the answers to these
questions:

1. Modern language features (e.g. pattern matching over algebraic data
types) are so difficult to implement that it is a practical impossibility
to expect ordinary programmers to use Lisp's extensibility to make
something decent out of it. Instead, the vast majority of programmers
choose to use less extensible languages that already provide most of what
they need (e.g. a powerful static type system) because that is vastly more
productive. In theory, this problem could be fixed but, in practice, the
few remaining members of the Lisp community lack the talent to build even
the most basic infrastructure (e.g. a concurrent GC).

2. Even though Lisp's forte is as a language laboratory, Lisp has so little
to offer but costs so much even in that niche that the implementors of
successful modern languages soon stripped all traces of Lisp from their
implementations in order to obtain decent performance (compilers written in
Lisp are extremely slow because Lisp is extremely inefficient).

In other words, Lisp is just a toy language because it does not help real
people solve real problems.

Robert Maas, http://tinyurl.com/uh3t

unread,
Jun 22, 2008, 1:04:56 AM6/22/08
to
> >> The more power a language gives me to compress my algorithm --
> >> both code and data, as well as in space and executuion time -- the
> >> more I like it.
> > Hopefully you accept that Lisp (specifically Common Lisp) is the
> > best language in this sense?
> From: Jon Harrop <j...@ffconsultancy.com>

> If that were true Lisp would be concise but Lisp is actually
> extremely verbose.

Compared to what? What other programming language do you know that
is as expressive as Lisp, allowing not just canned procedures
manually defined by syntax that is compiled, but ease in building
your own new procedures "on the fly" at runtime, but which is less
verbose than Lisp for equivalent value? Both C and Java are more
verbose than Lisp. To add two numbers in Lisp, you simply write (+
n1 n2) where n1 and n2 are the two numbers, and enter that directly
into Lisp's REP (Read Eval Print) loop. To do the same thing in C,
you need to write a function called "main" which includes both the
arithemetic operation itself also an explicit formatted-print
statement. To do the same thing in Java, you need to define a Class
which contains a method called "main", and then the innerds of main
are essentially the same as in C. For either C or Java, you then
need to compile the source, you can't just type it into a REP loop.
And in the case of Java, you can't even directly run the resultant
compiled-Class program, you have to start up the Java Virtual
Machine and have *it* interpret the main function of your compiled
Class.

Here's a more extreme comparison: Suppose you want to hand-code a list
of data to be processed, and then map some function down that list.
In Lisp all you need to do is
(mapcar #'function '(val1 val2 val3 ... val4))
where the vals are the expressions of the data you want processed
and function is whatever function you want applied to each element
of the list. You just enter that into the REP and you're done. Try
to imagine how many lines of code it takes in C to define a STRUCT
for holding a linked-list cell of whatever datatype you want the
function applied to, and a function for allocating a new cell and
linking it to some item of data and to the next cell in the chain,
and then calling that function over and over to add the cells to
the linked list one by one, and then you have to write a function
to map down the list to apply the other function. Or you have to
manually count how many items you want to process, and use an array
instead of a linked list, and manually insert elements into the
array one by one, then allocate another array to hold the results,
and finally you can
do (i=0,i<num,i) res[i]=fun(data[i]);
and then to print out the contents of that array or linked list you
have to write another function. And in Java you have to decide
whether to use vectors or arrays or any of several other collection
classes to hold your data, and then manually call
collection.add(element) over and over for the various elements you
want to add. Then to map the function down the list you need to
create an iterator for that collection and then alternate between
checking whether there any other elements and actually getting the
next element, and create another collection object to hold the
results. Then again just like in C you need to write a function for
reading out the elements in the result collection.

Of course if you're just going to map a function down your list and
immediately discard the internal form of the result, you don't need
a result list/array/collection. I'm assuming in the descriptions
above that you want to actually build a list/array/collection of
results because you want to pass *that* sequence of values to yet
another function later.

Are you complaining about the verbosity of the names of some of the
built-in functions? For example, there's adjust-array alphanumericp
assoc assoc-if char-lessp char-greaterp char-equal char-not-lessp
char-not-equal char-not-greaterp? Would you rather be required to
memorize Unix-style ultra-terse-inscrutable names aa a as ai cl cg
ce cnl cne cng respectively? Do you really think anybody will
understand a program that is written like that?

> 1. Why has Lisp always remained so unpopular when, as you say, it
> is so extensible?

Unlike all other major languages except Java and
HyperCard/HyperTalk and Visual Basic, it doesn't produce native
executables, it produces modules that are loaded into an
"environment". That means people can't just download your compiled
program and run it directly on their machine. They need to first
download the Lisp environment, and *then* your application can be
loaded into it (possibly by a script you provided for them) and
run. Unlike Visual Basic and Java, there's no major company pushing
hard to get people to install the environment. Unlike
HyperCard/HyperTalk, there's no major vendor of operating systems
(MS-Windows, FreeBSD Unix, Linux, Apple/Macintosh) providing Lisp
as part of the delivered operating system. (Linux does "ship" with
GNU Emacs with built-in E-Lisp, but when I say "Lisp" here I mean
Common Lisp. E-Lisp doesn't catch on, despite "shipping" with
Linux, because it simply doesn't have the usefulness of Common Lisp
for a wide variety of tasks *other* than managing a text-editor
with associated utilities such as DIRED and e-mail.) So Lisp has an
uphill battle to compete with MicroSoft and Sun who are pushing
their inferior languages. (HyperTalk/HyperCard died a long time ago
because it wasn't an especially good programming language, was
supplied only on Macintosh computers, and Macintosh lost market
share to MicroSoft's unfair labor practices, resulting in hardly
anybody making major use of it, resulting in Apple no longer
maintaining it to run under newer versions of their operating
system, so that now very few people still are running old versions
of MacOS where HyperCard runs.) Lisp is not yet dead. Common Lisp
is still thriving, even if it hasn't proven its case to the average
customer of MicroSoft Windows sufficiently that said customer would
want to install a Lisp environment so as to be able to run Lisp
applications.

> 2. Why have the implementors of successful modern languages that
> were originally built upon Lisp gone to great lengths to completely
> remove Lisp from their implementations?

-a- Penny wise pound foolish business managers who care only about
next-quarter profit, damn any longterm prospect for software
maintenance.
-b- Not-invented-here syndrome. Companies would rather base their
product line on a new language where they have a monopoly on
implementation rather than an already-existing well
established language where other vendors already provide
adequate implmentation which would need to be licensed for use
with your own commercial product line.
-c- Narrow-minded software vision which sees only today's set of
applications that can be provided by a newly-invented-here
language, blind to the wider range of services already
provided by Common Lisp that would support a greatly extended
future set of applications. Then once the company has invested
so heavily in building their own system to duplicate just some
of the features of Common Lisp, when they realize they really
do need more capability, it's too late to switch everything to
Common Lisp, so they spend endless resources crocking one new
feature after another into an ill-conceived system (compared
to Common Lisp), trying desperately to keep up with the needs
of the new applications they too-late realize they'll want.

> Modern language features (e.g. pattern matching over algebraic
> data types) are so difficult to implement that it is a practical
> impossibility to expect ordinary programmers to use Lisp's
> extensibility to make something decent out of it.

Agreed. That's why *one* (1) person or group needs to define
precisely what API-use-cases are required
(see the question I asked earlier today in the
64-parallel-processor thread, and please answer it ASAP)
and what intentional datatypes are needed for them, then implement
those API-use-cases per those intentional datatypes in a nicely
designed and documented package, then make that available at
reasonable cost (or free). I take it you, who realize the need,
aren't competant enough to implement it yourself, right? Why don't
you specify precisely what is needed (API-use-cases) and then ask
me whether I consider myself competant to implement your specs, and
offer to pay me for my work if I accept the task?

> Instead, the vast majority of programmers choose to use less
> extensible languages that already provide most of what they need
> (e.g. a powerful static type system)

That's rubbish!!! Static type declarations/checking only enforces
*internal* data types, not intentional data types on top of them.
So you're in a dilemma:
- Try to hardwire into the language every nitpicking difference in
intentional datatype as an actual *internal* datatype, with no
flexibility for the application programmer to include a new
datatype you didn't happen to think of.
- Hardwire into the language a meta-language capable of fully
expressing every nitpicking nuance of any intentional datatype,
so that application programmers can define their own *internal*
datatypes to express their intentional datatypes. Expect every
application programmer to actually make use of this facility,
always implementing new intentional datatypes as actual
application-programmer-defined *internal* datatypes.
- Don't bother to implement intentional datatypes at all.
Type-check just the internal datatypes that are built-in, and
expect the programmer to do what he does now of runtime
type-checking for all intentional type information, thereby
removing any good reason to do compile-time static type-checking
or even declaractions in the first place.

Example of internal datatype: 4.7

Example of intentional datatype: 4.7 miles per hour, 4.7 m/s, 4.7
children per average family, 4.7 average percentage failure rate of
latest-technology chips, $4.70 cost of gallon of gasoline, $7.7
million dollars total expenditure for a forest fire, $4.7 billion
dollars crop losses in the MidWest, $4.7 billion dollars per month
for war in Iraq, etc.

Application where you may need to mix the same internal datatype
with multiple intentions, where the intention is carried around
with the data to avoid confusion: Some engineers are working in
"metric" while others are working in English units, while all
original information must be kept as-is for error-control purposes
rather than automatically converted to common units on input, yet
later in the algorithms conversion to common units must be
performed to interface to various functions/methods which do
processing of the data. This is especially important if some
figures are total money for project while other figures are money
per unit time (month or year) and they need to be compared in some
way.

> In theory, this problem could be fixed but, in practice, the few
> remaining members of the Lisp community lack the talent to build
> even the most basic infrastructure (e.g. a concurrent GC).

What does that have to do with static type checking????????????

Please write up a Web page that explains:
- What you precisely mean by "concurrent GC" (or find a Web page
that somebody else wrote, such as on WikiPedia, that says
exactly the same as what *you* mean, and provide the URL plus a
brief summary or excerpt of what that other Web page says).
- List several kind of applications and/or API tools that are
hampered by lack of whatever that means.
- Explain how existing GC implementations don't satisfy your
definition of "concurrent GC" and how they specifically are not
sufficient for those kinds of applications you listed.
Then post the URL of your Web page here and/or in the other thread
where I also asked about the API-use-cases that you lament are
missing from Lisp.

> 2. Even though Lisp's forte is as a language laboratory, Lisp has
> so little to offer but costs so much

Um, some implementations of Common Lisp are **free** to download
and then "use to your heart's content". How does that cost too much???

> ... compilers written in Lisp are extremely slow because Lisp is
> extremely inefficient

That's a fucking lie!!

> In other words, Lisp is just a toy language because it does not
> help real people solve real problems.

That's another fucking lie!! I use Lisp on a regular basis to write
applications of practical importance to me, and also to write Web
demos of preliminary ideas for software I offer to write for
others. For example, there's a demo of my flashcard program on the
Web, including both the overall algorithm for optimal chronological
presentation of drill questions
(to get them into your short-term memory then to develop them
toward your medium-term and long-term memory),
and the specific quiz type where you type the answer to a question
(usually a missing word in a sentence or phrase)
and that short-answer quiz-type software coaches you toward a
correct answer and then reports back to the main drill algorithm
whether you needed help or not to get it correct. My Lisp program
on my Macintosh was used to teach two pre-school children how to
read and spell at near-adult level, and later the conversion of it
to run under CGI/Unix allowed me to learn some Spanish and
Mandarin, hampered only by lack of high-quality data to use to
generate Spanish flashcards and lack of anybody who knows Mandarin
and has the patience to let me practice my Mandarin with them. I'd
like to find somebody with money to pay me to develop my program
for whatever the money-person wants people to learn.

Robert Maas, http://tinyurl.com/uh3t

unread,
Jun 22, 2008, 5:31:50 AM6/22/08
to
> From: Your Name <n...@none.none>

> I have to admit that I haven't used Lisp since college. At the
> time, I found it interesting, and I seem to recall that it was well
> suited for things like AI development.

That's an incredible understatement, like saying you haven't used
an automobile since college when you found it useful for taking
high-school girls to drive-in movies (but you can't think of any
other use whatsoever for an automobile).

Why don't you admit that Lisp is a general-purpose programming
language that is useful for many different kinds of applications
that require fiexible datastructures to be crafted on the fly at
runtime? Can't you think of any other application area except A.I.
that might make good use of such data structures?

> I also remember it being difficult to write readable code.

Either you had a bad instructor, or you didn't have any inherent
talent, or both. It's a lot easier to write readable code in Lisp
than in other popular languages such as C or even Java. For
example, here's a simple executable expression in Lisp:
(loop for str in
'("Hello" "world." "Always" "love" "Lisp.")
collect (position #\l str :test #'char-equal))
which returns the list:
(2 3 1 0 0)
Now try to convert that to C so that the result is more "readable".
It has to be an expression which returns a value. It's OK if you
need to write a function which returns a value, then give a line of
code that calls that function as an "expression" that returns the
same value. It's *not* OK for you to write a program that
*prints*out* that syntax of open parens digits and spaces and close
parens but doesn't build any actual list of numeric values to
return to the caller. Go ahead and see if you can write anything
even half as clean as the three lines of Lisp code I displayed
above.
(By the way, after completely composing that three-line
expression, I actually started up CMUCL and copied the three
lines from my message-edit buffer and pasted it into CMUCL, and
it ran correctly the first time, and then I copied the result
from CMUCL and pasted it back into the edit buffer. Try writing
your C equivalent program and getting it exactly right the very
first time you try to compile it, nevermind the original question
of making it "readable".)

> Professionally, I'm somewhat handcuffed to C and C++ for reasons
> I mentioned earlier.

I feel sad for your plight. But I presume you're an adult, so
you're responsible for allowing yourself to be abused in that way.

Why don't you organize your fellow workers to stage a strike
against your employer until you are un-handcuffed? Or just file a
complaint with whatever government agecy protects employees from
abuse?

> It's also hard to find bright and motivated employees who are
> fluent in Lisp.

That's completely untrue if you mean *potential* employees, people
your company *could* hire if they had any brains. But it's true if
you mean *present* emplyees of your company. If that's what you're
saying, why don't you convince your company to hire somebody new
instead of recruiting only from their existing employee base?

> I'd just like a language where this idea of compression, or
> refactoring, is the central principle around which the language is
> built.

While I'm totally in favor of constant-refactoring as an
operational principle, I'm not in favor of making the language
itself *require* that way of working. For example, suppose you have
a government contract where every nitpicking detail of the use
cases are specified in the wording of the contract, and you are
legally required to provide *exactly* what the contract requires.
In that case you might be able to design the entire application at
the start and *not* need to do any refactoring during development.
Why should you be *required* by the language design to refactor
without any benefit? IMO it's better to have a language that makes
easy to refactor several times per day without being the central
principle of the language that you just can't escape.

You talk about being handcuffed to C and C++. Being handcuffed to
perpetual refactoring would not be as painful, but still I'd rather
avoid that too. Why do you seem to *want* it?

Common Lisp is the language of choice for enabling frequent
refactoring without absolutely requiring it, without refactoring
being the central principle of the language per se, merely *a*
central principle of the REP which is available whenever you need
it.

> Any language that supports subroutines offers a mechanism for
> this, but I feel the concept could be taken further.

I agree only to a limited degree. Having a generic datatype which
is the default, with runtime dispatching on type specified by the
application programmer, as in Lisp, is a lot better for this
purpose than most languages-with-subroutines which have strict
classification of data types that essentially preclude developing
as if there were a generic data type.

> It's all very nebulous at the moment, but I feel that somehow the
> answer lies in pointers.

That's half true. The other half is automatic garbage collection,
which works only with safe runtime datatypes (as in Lisp, and Java
if you declare variables of type java.lang.Object and use the
'instanceof' operator to dispatch on expected types your code knows
how to process). Unconstrained pointer arithmetic as in C is a
maintainability **disaster**. Look for example at the
buffer-overflow bugs (in code written in C) that allow
viruses/worms to take over people's computers and use them to
replicate themselves and also to disclose confidential information
to criminal organizations in foreign countries.

> At its heart, a subroutine is nothing more than a pointer, in any
> language.

How can I even begin to explain to you how grossly wrong you are???

In Lisp, a subroutine (function) is a first-class-citizen fully
self-contained data object. I'm not sure if Java goes that far, but
clearly in Java a subroutine (method) is likewise some kind of
self-contained data object. In neither case is it just a pointer to
some random spot in RAM as you claim. Only in really stupid cruddy
languages like C is a subroutine just a block of code with a
pointer to (the first byte of) it.

> A compiler takes the code inside the routine and stuffs it into
> memory somewhere. To call that function, you just dereference the
> pointer to "jump" to that code.

Only in the really stupid cruddy languages such as C you're
familiar with. In a decent language, there are stack frames, that
are formal structures, which are carefully controlled during call
of a function and return from a function as well as non-local
return (throw/catch and error restarts).

Even in C and assembly language, you don't JUMP to a subroutine,
you JSR to a subroutine, which automatically puts the return
address within the calling program onto the stack (or into a
machine register on some CPUs).

> An object in an OO language is accomplished using a pointer.
> Each class has a function table, and each object has a pointer to
> that function table. Here, the compression is happening at the
> compiler level. Since every object of a given class has the same
> code, it can all be moved to a common place, then each object need
> only store a pointer to it. Further, code in a base class need only
> exist there, with each child class pointing to that table.

OK, that paragraph is actually mostly correct and nicely explained.

> But these are hard-coded abstractions built into compilers. C++
> or Java tell you, "This is how inheritance works... This is the
> order in which constructors are called..." Somehow, I'd like for
> the language itself to make the mechanism directly accessible to
> the programmer.

That's pretty easy in Lisp. Are you familar with the 'typecase'
macro? You are free not to use the built-in CLOS inheritance at
all, but instead to use CLOS classes only to tag objects for
purpose of dispatching via the 'typecase' macro. You have the
choice whether to have it all nice and automatic via inheritance,
or explicitly under control of the programmer via 'typecase'
dispatching. There are arguments pro and con each way of doing it.
Within the past week or so somebody posted a major complaint about
OOP that with a deeply nested class inheritance hierarchy it's nigh
impossible for anyone to figure out why a particular method defined
in some distantly-related class is being called when a generic
function is passed some particular object of some particular class.
(In Java, read "generic function" as "method name", which is part
of the "method signature". Quoting from Liang's textbook (ISBN
0-13-100225-2) page 119: "The <i>parameter profile</i> refers to
the type, order, and number of parameters of a method. The method
name and the parameter profiles together constitute the <i>method
signature</i>. I like the way that author expresses concepts in Java.)

> If his or her programming style then naturally tends to evolve
> into an OO sort of structure, wonderful. If not, then maybe a
> sort of table-driven type of "engine" architecture would emerge.
> That just happens to be what I generally find most powerful.

Do you agree that Common Lisp offers the best availability of
features to support not just standard OO design (making heavy use
of inheritance) but also other variants that you seem to prefer
sometimes?

> I suppose you could just take a huge step backwards and write
> pure assembly language.

NO NO A THOUSAND TIMES NO!!!
Try Common Lisp, really, try it, for J.Ordinary applications of the
day. Whatever new application you want to write this coming Monday,
try writing it with Common Lisp, and tell me what difficulties (if
any) you experience.

Hey, if you are unwilling to swallow crow, then write a
machine-language emulator in Common Lisp, and then try to write
applications in that emulated machine language. Make sure your
emulator has debugging features a million times better than what
you'd have a bare machine you're trying to program in machine
language. While you're at it, put your emulator up as a CGI service
so that the rest of us can play with it too.

Suggestion: Emulate IBM 1620 machine language.
16 00010 00000
That's your first test program, just a single instruction.

> Then all you really have is code, data, and pointers. You're
> free to use them however you like. But I strongly believe there is
> a way to still offer this freedom, while also offering a great deal
> of convenience, readability, and maintainability by way of a
> high-level language.

If Lisp were Maynard G. Krebs
<http://www.fortunecity.com/meltingpot/lawrence/153/krebs.html>
<http://althouse.blogspot.com/2005/09/maynard-to-god-you-rang.html>
Lisp would jump in right there and say "YOU RANG?"

Jon Harrop

unread,
Jun 22, 2008, 8:13:13 AM6/22/08
to
Robert Maas, http://tinyurl.com/uh3t wrote:
>> >> The more power a language gives me to compress my algorithm --
>> >> both code and data, as well as in space and executuion time -- the
>> >> more I like it.
>> > Hopefully you accept that Lisp (specifically Common Lisp) is the
>> > best language in this sense?
>> From: Jon Harrop <j...@ffconsultancy.com>
>> If that were true Lisp would be concise but Lisp is actually
>> extremely verbose.
>
> Compared to what? What other programming language do you know that
> is as expressive as Lisp, allowing not just canned procedures
> manually defined by syntax that is compiled, but ease in building
> your own new procedures "on the fly" at runtime, but which is less
> verbose than Lisp for equivalent value?

Haskell, SML, OCaml, Mathematica, F# and Scala all allow real problems to be
solved much more concisely than with Lisp. Indeed, I think it is difficult
to imagine even a single example where Lisp is competitively concise.

> Both C and Java are more
> verbose than Lisp. To add two numbers in Lisp, you simply write (+
> n1 n2) where n1 and n2 are the two numbers, and enter that directly
> into Lisp's REP (Read Eval Print) loop.

Yes. Consider the trivial example of defining a curried "quadratic"
function. In Common Lisp:

(defun quadratic (a) (lambda (b) (lambda (c) (lambda (x)
(+ (* a x x) (* b x) c)))))

In F#:

let inline quadratic a b c x = a*x*x + b*x + c

> To do the same thing in C,
> you need to write a function called "main" which includes both the
> arithemetic operation itself also an explicit formatted-print
> statement. To do the same thing in Java, you need to define a Class
> which contains a method called "main", and then the innerds of main
> are essentially the same as in C. For either C or Java, you then
> need to compile the source, you can't just type it into a REP loop.
> And in the case of Java, you can't even directly run the resultant
> compiled-Class program, you have to start up the Java Virtual
> Machine and have *it* interpret the main function of your compiled
> Class.

Forget C and Java.

> Here's a more extreme comparison: Suppose you want to hand-code a list
> of data to be processed, and then map some function down that list.
> In Lisp all you need to do is
> (mapcar #'function '(val1 val2 val3 ... val4))
> where the vals are the expressions of the data you want processed
> and function is whatever function you want applied to each element
> of the list. You just enter that into the REP and you're done.

Lisp:

(mapcar #'function '(val1 val2 val3 ... val4))

OCaml and F#:

map f [v1; v2; v3; .... vn]

Mathematica:

f /@ {v1, v2, v3, ..., vn}

> Try
> to imagine how many lines of code it takes in C to define a STRUCT
> for holding a linked-list cell of whatever datatype you want the
> function applied to, and a function for allocating a new cell and
> linking it to some item of data and to the next cell in the chain,
> and then calling that function over and over to add the cells to
> the linked list one by one, and then you have to write a function
> to map down the list to apply the other function. Or you have to
> manually count how many items you want to process, and use an array
> instead of a linked list, and manually insert elements into the
> array one by one, then allocate another array to hold the results,
> and finally you can
> do (i=0,i<num,i) res[i]=fun(data[i]);
> and then to print out the contents of that array or linked list you
> have to write another function. And in Java you have to decide
> whether to use vectors or arrays or any of several other collection
> classes to hold your data, and then manually call
> collection.add(element) over and over for the various elements you
> want to add. Then to map the function down the list you need to
> create an iterator for that collection and then alternate between
> checking whether there any other elements and actually getting the
> next element, and create another collection object to hold the
> results. Then again just like in C you need to write a function for
> reading out the elements in the result collection.

Forget C and Java. Compare with modern alternatives.

> Of course if you're just going to map a function down your list and
> immediately discard the internal form of the result, you don't need
> a result list/array/collection. I'm assuming in the descriptions
> above that you want to actually build a list/array/collection of
> results because you want to pass *that* sequence of values to yet
> another function later.
>
> Are you complaining about the verbosity of the names of some of the
> built-in functions? For example, there's adjust-array alphanumericp
> assoc assoc-if char-lessp char-greaterp char-equal char-not-lessp
> char-not-equal char-not-greaterp? Would you rather be required to
> memorize Unix-style ultra-terse-inscrutable names aa a as ai cl cg
> ce cnl cne cng respectively? Do you really think anybody will
> understand a program that is written like that?

Far more programmers use modern functional languages like Haskell, OCaml and
F# than Lisp now. They clearly do not have a problem with the supreme
brevity of these languages.

Look at the intersection routines from my ray tracer benchmark, for example.
In OCaml:

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

and in 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)))

The comparative brevity of the OCaml stems almost entirely from pattern
matching.

>> 1. Why has Lisp always remained so unpopular when, as you say, it
>> is so extensible?
>
> Unlike all other major languages except Java and
> HyperCard/HyperTalk and Visual Basic, it doesn't produce native
> executables, it produces modules that are loaded into an
> "environment". That means people can't just download your compiled
> program and run it directly on their machine.

IIRC, some commercial Lisps allow standalone executables to be generated but
they are still not popular.

> They need to first
> download the Lisp environment, and *then* your application can be
> loaded into it (possibly by a script you provided for them) and
> run. Unlike Visual Basic and Java, there's no major company pushing
> hard to get people to install the environment. Unlike
> HyperCard/HyperTalk, there's no major vendor of operating systems
> (MS-Windows, FreeBSD Unix, Linux, Apple/Macintosh) providing Lisp
> as part of the delivered operating system. (Linux does "ship" with
> GNU Emacs with built-in E-Lisp, but when I say "Lisp" here I mean
> Common Lisp. E-Lisp doesn't catch on, despite "shipping" with
> Linux, because it simply doesn't have the usefulness of Common Lisp
> for a wide variety of tasks *other* than managing a text-editor
> with associated utilities such as DIRED and e-mail.) So Lisp has an
> uphill battle to compete with MicroSoft and Sun who are pushing
> their inferior languages. (HyperTalk/HyperCard died a long time ago
> because it wasn't an especially good programming language, was
> supplied only on Macintosh computers, and Macintosh lost market
> share to MicroSoft's unfair labor practices, resulting in hardly
> anybody making major use of it, resulting in Apple no longer
> maintaining it to run under newer versions of their operating
> system, so that now very few people still are running old versions
> of MacOS where HyperCard runs.) Lisp is not yet dead. Common Lisp is still

> thriving...

Lisp is not "thriving" by any stretch of the imagination. According to
Google Trends (which measures the proportion of searches for given search
terms) "Common Lisp" has literally almost fallen off the chart:

http://www.google.com/trends?q=common+lisp

>> 2. Why have the implementors of successful modern languages that
>> were originally built upon Lisp gone to great lengths to completely
>> remove Lisp from their implementations?
>
> -a- Penny wise pound foolish business managers who care only about
> next-quarter profit, damn any longterm prospect for software
> maintenance.
> -b- Not-invented-here syndrome. Companies would rather base their
> product line on a new language where they have a monopoly on
> implementation rather than an already-existing well
> established language where other vendors already provide
> adequate implmentation which would need to be licensed for use
> with your own commercial product line.
> -c- Narrow-minded software vision which sees only today's set of
> applications that can be provided by a newly-invented-here
> language, blind to the wider range of services already
> provided by Common Lisp that would support a greatly extended
> future set of applications. Then once the company has invested
> so heavily in building their own system to duplicate just some
> of the features of Common Lisp, when they realize they really
> do need more capability, it's too late to switch everything to
> Common Lisp, so they spend endless resources crocking one new
> feature after another into an ill-conceived system (compared
> to Common Lisp), trying desperately to keep up with the needs
> of the new applications they too-late realize they'll want.

I have never heard of a single user of a modern FPL regretting not choosing
Lisp. Can you refer me to any such people?

>> Modern language features (e.g. pattern matching over algebraic
>> data types) are so difficult to implement that it is a practical
>> impossibility to expect ordinary programmers to use Lisp's
>> extensibility to make something decent out of it.
>
> Agreed. That's why *one* (1) person or group needs to define
> precisely what API-use-cases are required
> (see the question I asked earlier today in the
> 64-parallel-processor thread, and please answer it ASAP)
> and what intentional datatypes are needed for them, then implement
> those API-use-cases per those intentional datatypes in a nicely
> designed and documented package, then make that available at
> reasonable cost (or free). I take it you, who realize the need,
> aren't competant enough to implement it yourself, right?

You are grossly underestimating the amount of work involved. It would
literally take me decades of full time work to catch up with modern
functional language implementations in terms of features and the result
could never be competitively performant as long as it was built upon Lisp.
Finally, I don't believe I could ever build a commercial market as
successful as F# already is so, even if I did ever start doing this, it
would be as a hobby and not for profit.

Note that this is precisely why the developers of all successful modern
functional language implementations do not build them upon Lisp.

> Why don't
> you specify precisely what is needed (API-use-cases) and then ask
> me whether I consider myself competant to implement your specs, and
> offer to pay me for my work if I accept the task?

Because it would be a complete waste of my time and money because Lisp
offers nothing of benefit whatsoever to me, my company or our customers.
Moreover, Lisp does not even have a commercially viable market for our kind
of software.

Lisp is literally at the opposite end of the spectrum from where we want to
be. We need to combine the performance of Fortran with the expressiveness
of Mathematica (which F# almost does!) but Lisp combines the performance of
Mathematica with the expressiveness of Fortran.

>> In theory, this problem could be fixed but, in practice, the few
>> remaining members of the Lisp community lack the talent to build
>> even the most basic infrastructure (e.g. a concurrent GC).
>
> What does that have to do with static type checking????????????

That has nothing to do with static typing. I was listing some of Lisp's most
practically-important deficiencies. Lack of static typing in the language is
one. Lack of concurrent GC in all Lisp implementations is another. Also
lack of threads, weak references, finalizers, asychronous computations,
memory overflow recovery, tail call optimization, callcc etc. are all
fundamental deficiencies of the language.

> Please write up a Web page that explains:
> - What you precisely mean by "concurrent GC" (or find a Web page
> that somebody else wrote, such as on WikiPedia, that says
> exactly the same as what *you* mean, and provide the URL plus a
> brief summary or excerpt of what that other Web page says).

See Jones and Lins "Garbage Collection: algorithms for automatic dynamic
memory management" chapter 8.

In a serial GC, the program often (e.g. at every backward branch) calls into
the GC to have some collection done. Collections are typically done in
small pieces (incrementally) to facilitate soft-real time applications but
can only use a single core. For example, OCaml has a serial GC so OCaml
programs wishing to use multiple cores fork multiple processes and
communicate between them using message passing which is two orders of
magnitude slower than necessary, largely because it incurs huge amounts of
copying that is not necessary on a shared memory machine:

http://caml.inria.fr/pub/ml-archives/caml-list/2008/05/6ba948d84934b1e61875687961706f61.en.html

In a parallel GC, the program occasionally (e.g. when a minor heap is
exhausted) suspends all program threads and begins a parallel traversal of
the heap using all available cores. This allows programs (even serial
programs) to benefit from multiple cores but it has poor incrementality (so
it is unsuitable for soft real-time applications) and scales badly. For
example, the GHC implementation of Haskell recently acquired a parallel GC
which can improve Haskell's performance on <4 cores but (according to the
authors) can degrade performance with more cores because the cost of
suspending many threads becomes the bottleneck.

With a concurrent GC, the garbage collector's threads run concurrently with
the program threads without globally suspending all program threads during
collection. This is scalable and can be efficient but it is incredibly
difficult to implement correctly. The OCaml team spent a decade trying to
implement a concurrent GC and never managed to get it working, let alone
efficient.

> - List several kind of applications and/or API tools that are
> hampered by lack of whatever that means.

Any software that requires fine-grained parallelism for performance will be
hampered by the lack of a concurrent GC.

>> 2. Even though Lisp's forte is as a language laboratory, Lisp has
>> so little to offer but costs so much
>
> Um, some implementations of Common Lisp are **free** to download
> and then "use to your heart's content". How does that cost too much???

Development costs are astronomical in Lisp compared to modern alternatives
like F#, largely because it lacks a static type system but also because it
lacks language features like pattern matching, decent developer tools like
IDEs and libraries like Windows Presentation Foundation (WPF).

For example, adding the new 3D surface plotting functionality to our F# for
Visualization product took me four days of full time work even though I am
new to WPF:

http://www.ffconsultancy.com/products/fsharp_for_visualization/?clp

The result will run reliably on hundreds of millions of computers (you just
need Windows and .NET 3.0 or better). Developing it into a standalone
Windows application will be trivial, if I choose to do so.

Contrast that with Lisp. There are no decent Lisp implementations for .NET.
Microsoft certainly aren't using or advocating Lisp. So you can immediately
kiss goodbye to easy multicore support and all of Microsoft's latest
libraries and tools. You'll be developing your GUIs without the aid of an
interactive GUI designer and you'll be using a low-level graphics API like
DirectX or OpenGL for visualization. You are looking at several times as
much effort to get something comparable and, even then, it will never be as
reliable (because Microsoft have invested billions in making WPF and .NET
reliable).

Finally, there is no way you'll ever turn a profit because the market for
commercial third-party software for Lisp is too small.

>> In other words, Lisp is just a toy language because it does not
>> help real people solve real problems.
>

> ...I use Lisp on a regular basis to write


> applications of practical importance to me, and also to write Web
> demos of preliminary ideas for software I offer to write for
> others. For example, there's a demo of my flashcard program on the
> Web, including both the overall algorithm for optimal chronological
> presentation of drill questions
> (to get them into your short-term memory then to develop them
> toward your medium-term and long-term memory),
> and the specific quiz type where you type the answer to a question
> (usually a missing word in a sentence or phrase)
> and that short-answer quiz-type software coaches you toward a
> correct answer and then reports back to the main drill algorithm
> whether you needed help or not to get it correct. My Lisp program
> on my Macintosh was used to teach two pre-school children how to
> read and spell at near-adult level, and later the conversion of it
> to run under CGI/Unix allowed me to learn some Spanish and
> Mandarin, hampered only by lack of high-quality data to use to
> generate Spanish flashcards and lack of anybody who knows Mandarin
> and has the patience to let me practice my Mandarin with them. I'd
> like to find somebody with money to pay me to develop my program
> for whatever the money-person wants people to learn.

I think you should aspire to earn money directly from customers rather than
asking people to give you money to develop your software.

Robert Maas, http://tinyurl.com/uh3t

unread,
Jun 22, 2008, 6:05:40 PM6/22/08
to
> From: thomas.mer...@gmx.at

> If you want to invent your own abstraction mechanisms you might
> be interested in Seed7. There are several constructs where the
> syntax and semantic can be defined in Seed7:
> - Statements and operators (while, for, +, rem, mdiv, ... )

Some of those are such commonly useful constructs that it seems
ill-conceived to have each application programmer re-invent the
wheels. Are there standard packages available to provide these as
"givens" with a well-documented API so that different application
programmers can read each other's code?

Now the ability to define *variants* of those common operators
which do additional/different tasks would be useful. Obviously if
the standard operator can be defined, a variant can be defined,
right?

> - Abstract data types (array, hash, bitset, ... )

Again, these are such basic container types that they really ought
to be provided in a standard package. Are they? Again, the ability
to define variants on the standard implementation would be useful.
But the ability to mix-in the variation within the standard
definition without needing to start from scratch to re-invent the
wheel would be even better. Is that possible? Do the standard
definitions have hooks for adding variant functionality, sort of
the way Common Lisp has hooks for what to do when an error occurs
or an unbound variable is referenced or a garbage-collect happens
etc.? For example, is it possible to use a built-in (standard
package) definition of hash table but change the pre-hash function
(the pseudo-random function from key to large integer) to replace
the standard pre-hash function?

> Seed7 Homepage: http://seed7.sourceforge.net

] It is a higher level language compared to Ada, C/C++ and Java.

Um, C is not exactly a high-level language.
Comparing your language to both C and Java in the same sentence is
almost an oxymoron.

] The compiler compiles Seed7 programs to C programs which are
] subsequently compiled to machine code.

Ugh! So you're using C almost as if it were an assembly language,
which is probably appropriate for it *not* being a high-level
language itself, being in reality a "syntax-sugared assembly
language". OK, you win, I withdraw my complaint, if you stipulate
that your earlier statement really was self-contradictory.

If you don't allow that C is nothing more than a sugar-coated
assembly language, if you insist it's really a high-level language,
then the Seed7 compiler doesn't need to really do anything, just do
a syntax transformation from one high-level language to another, in
which case you might do better to syntax-translate to Common Lisp,
maybe just emulate the Seed7 syntax within CL as if it were a DSL
(Domain-Specific Language).

] Functions with type results and type parameters are more elegant than
] a template or generics concept.

Since a template concept is dumb to begin with, saying you're
better than that is not a really good advertising point, like
saying you as a person have higher moral standards than Adolph
Hitler or Pol Pot or George W. Bush.

I don't know what you mean by a "generics" concept. Is that like
the tagging of data object to identify their respective data types
at run-time that Lisp has? Or something completely different?
Please define what precisely you mean by that (I assume you're the
author of that Web page).

] * Types are first class objects

In a sense that's also true in Common Lisp:
(class-of 5)
=> #<BUILT-IN-CLASS FIXNUM (sealed) {50416FD}>
(class-of (expt 3 99))
=> #<BUILT-IN-CLASS BIGNUM (sealed) {5044695}>
(class-of (make-hash-table))
=> #<STRUCTURE-CLASS HASH-TABLE {500D32D}>
* (defclass foo)
=> #<STANDARD-CLASS FOO {90285FD}>
Is that the kind of first-class type-objects that you are talking about?

] (Templates and generics can be defined easily without special syntax).

What precisely do you mean by "templates"?
What precisely do you mean by "generics"?

Suggestion: On a Web page where you throw around terms like this,
each such mention of a jargon-term should actually be
<a href="urlWhereTheTermIsDefined">theTerm</a>
That's the nice thing about WebPages (and DynaBooks, if they ever
existed), that there can be links from jargon to definitions, not
possible in hardcopy printed books (footnotes for every such term
would be a royal pain by comparison with HREF anchors).

Seed7 - The extensible programming language: User defined statements
and operators, abstract data types, templates without special
syntax, OO with interfaces and multiple dispatch, statically typed,
interpreted or compiled, portable, runs under linux/unix/windows.

] * User defined statements and operators.

Why make a distinction between statements and expressions-with-operators??
IMO it's a royal pain to have to deal with.
Lisp does it right, having every expression usable *both* as
statement or operator, or even as both simultaneously
(perform side-effect and also return a value, for example SETF
which stores the right-side into the left-side place but *also*
returns that value to the caller, so that SETFs can be nested to
store the same value in more than one place, and IF and CASE which
select which of several alternative blocks of code to execute and
*also* return the value from the block that was executed).
C does it wrong, requiring different syntax for IF statements and
?: expressions which return a value.

] * Static type checking and no automatic casts.

There's hardly any value to static type checking, compared to
dynamic (runtime) type-checking/dispatching. See another article I
posted late Friday night
<http://groups.google.com/group/comp.programming/msg/5cea7d186eddfd42>
= Message-ID: <rem-2008...@yahoo.com>
(skip down to where I used the word "dilemma", page 10 on VT100
lynx, appx. 5 screens into the article on full-screen browser)
where I explained why static type checking fails to solve the
problem it claims to solve hence is worthless to include in a
programming language.

By "no automatic casts", do you mean that you can't even have a
literal that is generic to cast to short-integer or long-integer in
an assignmet, so everytime you set a variable to a literal value
you must explitly tag the literal with the appropriate word length
(or even worse, explictly cast it from literal type to whatever
type the variable happens to be today)??

] * exception handling

How does your servive compare with what Common Lisp and Java provide?
Do you provide a default break loop for all uncaught exceptions, as
Lisp does, or do you do what Java does of ABEND/BACKTRACE whenever
an uncaught unchecked-exception ocurrs (and *require* compile-time
exception handler for each and every checked-exception).

] * overloading of procedures/functions/operators/statements

So that's basically the same as what C++ and Java do (and Common
Lisp generic functions do even better)?

] * Runs under linux, various unix versions and windows.

Does it run on FreeBSD Unix? (That's what my shell account is on.)
Why doesn't it run on MacOS 6 or 7? (Just curious, since my Mac
doesn't have a decent C compiler. I have Sesame C, but it's only a
crude subset, no structs or even arrays, not even malloc. It *does*
have machine-language hooks, whereby you can embed hexadecimal
codes inline, which I used to implement a crude form of malloc via
system/OS traps to allocate a huge block of RAM and then my own code
to break it into pieces to return to callers of myMalloc!!)

<http://seed7.sourceforge.net/faq.htm#new_language>
] Why a new programming language?
] Because Seed7 has several features which are not found in other
] programming languages:
] * The possibility to declare new statements (syntactical and
] semantically) in the same way as functions are declared

Are you really really sure that's a good idea. Like why is it
even necessary in most cases, compared to Lisp's system of keeping the
bottom-level syntax OpenParens OperatorName Arg1 Arg2 ... Argn
CloseParens but allowing various OperatorNames to cause the
apparent Args to be interepreted any way you like?
Problems with defining new statement-level operators:
- It makes one person's code unreadable by anyone else. Not just
that they don't know what the semantics do, another person can't
even parse the new syntax you've invented.
- It kills any chance of having a smart editor, such as Emacs,
automatically deal with sub-expressions, such as copy/cut/paste
entire sub-expressions, skip forward/backward by sub-expressions,
etc., unless you take the extra pain of reconfiguring Emacs to
know about all the new syntaxes you've invented for your Seed7
sourcecode.
- It's already a royal pain in the first place to need to keep a
copy of the top half of page 49 of K&R posted for constant
reference when deciding whether parentheses are really necessary
to provide the desired sequence of sub-expression combination.
It would be an order of magnitude more pain to need to keep a
listing of operator precedence for every new operator invented by
every programmer in a large software project, and know which page
to refer to when looking at each person's code, and not get all
confused when trying to coorelate two different pieces of code
written by different people which use different operator
definitions.

Please reconsider your decision to use operators in the first place
for anything except arithmetic expressions.
Please consider going back to square one in your syntax design, and
using Lisp notation for everything except arithmetic (with some
sort of syntactic marker to tell when arithmetic mode starts and
ends, i.e. to wrap an arithmetic-syntax expression within an
otherwise s-expression syntax).
Heck, consider scrapping the "new language from scratch, except C
as post-processor to compiler" idea entirely, instead just use a
reader macro within Common Lisp to nest an arithmetic-syntax
expression within an s-expression. Maybe something like this:
(let* ((origHeight (get-height myDoorway))
(aspectRatio (get-aspect-ratio standardDoor))
(scaledWidth #[origVal*aspectRatio])) ;Sub-expression in math syntax
(set-width myDoorway scaledWidth)
(make-new-door :height origHeight :width scaledWidth))
Does your current implementation even provide anything like LET* in
the first place?

Some/most/all of the other features you claim aren't available in
other languages are in fact already available in Common Lisp.

<http://seed7.sourceforge.net/faq.htm#bytecode>

] Can I use something and declare it later?
] No, everything must be declared before it is used. The possibility to
] declare new statements and new operators on one side and the static
] typing requirements with compile time checks of the parameters on the
] other side would make the job of analyzing expressions with undeclared
] functions very complex.

This is a killer for top-down design+debugging that sometimes is
useful. Better to stick to a fixed syntax, where something doesn't
need to be defined before code that calls it can be set up. Then
when an undefined-function exception throws you into the break
package, *then* you can supply the missing fuction definition and
proceed from the break as if nothing were wrong in the first place.

] Forward declarations help, if something needs to be used before it can
] be declared fully.

In practice they are a royal pain, both the necessity of doing them
all before you can even write the code that calls the undefind
functions, and then the maintenance problem if you change the
number of parameters to a function and therefore need to find all
the places in your code where you declared the old parameters and
now need to re-declare all of those and re-do everything that
follows. It's a totally royal pain to have to deal with!!

] With static type checking all type checks are performed during
] compile-time. Typing errors can be caught earlier without the need to
] execute the program. This increases the reliability of the program.

Bullshit. Utter bullshit!!!

See what I wrote (see URL/MessageID of article earlier above) about
the failure of static type checking to deal with intentional types
regardless of whether you try to definie every intentional type an
an explicitly declared static type or not. Then answer my point,
either by admitting you were totally mistaken in your grandoise
claim about static type checkign, or by explaining how Seed7 is
able to completely solve the problem.

] Can functions have variable parameter lists?
] No, because functions with variable parameter lists as the C printf
] function have some problems:
] * Type checking is only possible at run time.

That's untrue.

Variable args of the same type can be all checked by mapping the
checker down the list of formal arguments in the syntax.
Lisp doesn't provide this, but an extension to DEFUN/LAMBDA could do this:
(defun foo (i1(integer) f2(single-float) &nary cxs(complex)) ...)
(foo 5 4.2 #C(2 5) #C(1 4) #C(6 9)) ;OK
(foo 5 4.2 #C(2 5) 3.7 #C(6 9)) ;syntax error, 3.7 not COMPLEX

Keyword args can be checked to make sure the only keywords actually
used are in fact defined as available by the function definition.
Thus: (defun foo (a1 a2 &key k1 k2) ...)
(foo 5 7 :k2 42 :k3 99) ;syntax error, keyword K3 not allowed by FOO
;suggestion: K1 is allowed, maybe you meant that?

] Although functions can return arbitrary complex values (e.g. arrays of
] structures with string elements) the memory allocated for all
] intermediate results is freed automatically without the help of a
] garbage collector.

How?????

Debug-use case: A application is started. A sematic error (file
missing for example) throws user into break package. User fixes the
problem, but saves a pointer to some structure in a global for
later study. User continues from the break package. There are now
two pointers to the structure, the one the compiler provided on the
stack, which goes away when some intermediate-level function (above
the break) returns, and the global one set up by the user from the
break package. How does the return-from-function mechanism know
that the structure should *not* be freed? Later, when the user
changes that global to point somewhere else, and there are no
longer any references to that structure, how does the
assign-new-value-to-global mechanism know that the *old* value of
that global can *now* finally be freed?

Reference counts don't work if you allow circular pointer structures:
(setq foo (list 1 2 3))
=> (1 2 3)
(setf (cdddr foo) (cdr foo))
=> #1=(2 3 . #1#)
foo
=> (1 . #1=(2 3 . #1#))
(setq foo nil)
;It's easy to see that the CONS cell pointing at 1 can be freed,
; because it no longer has any references.
;But the cells pointing to 2 and 3 have CDR pointers to each other,
; so how does your system know that those cells can also be freed
; without a garbage collector to verify no other references except
; those circular references exist anywhere within the runtime environment??

] of all container classes. Abstract data types provide a better and
] type save solution for containers ...
**f* (typo, the first typo I've found so-far, your English is good!)

] What is multiple dispatch?
] Multiple dispatch means that a function or method is connected to more
] than one type. The decision which method is called at runtime is done
] based on more than one of its arguments. The classic object
] orientation is a special case where a method is connected to one class
] and the dispatch decision is done based on the type of the 'self' or
] 'this' parameter. The classic object orientation is a single dispatch
] system.

What you've implemeted sounds the same as generic functions in Common Lisp.

But having *any* runtime dispatching based on actual type of an
object defeats your <coughCough>wonderful</coughCough> static type
checking, since if three sub-types inherit from one parent type,
but only two of them implement a particular method, especially if
there are multiple parameter-type dispatching with multiple options
for each parameter and not *all* combinations of parameter types
are provided, then it's possible for compiler to accept a call
involving parent-type declared parameters which at runtime steps
into one the combinations of subtypes that aren't defined.

Example, in case my English wasn't clear:
Define class table with subtypes endtable dinnertable coffeetable and bedstand.
Define class room with subtypes livingroom bedroom kitchen and bathroom.
Declare generic function arrangeTableInRoom, and define these
specific cases of parameters to it:
endtable,livingroom
dinnertable,kitchen
bedstand,bedroom
endtable,bathroom
Declare variable t1 of class table.
Declare variable r1 of class room.
Assign t1 an object of sub-type bedstand.
Assign r1 an object of sub-type kitchen.
Call arrangeTableInRoom(t1,r1) ;Compiles fine, but causes runtime exception,
; because that specific method is not defined.
;Static type checking fails to detect this type-mismatch undefined-method error.

] As in C++, Java, C# and other hybrid object oriented languages there
] are predefined primitive types in Seed7. These are integer, char,
] boolean, string, float, rational, time and others.

What is the precise meaning of type 'integer'? Is it 16-bit signed
integer, or 32-bit signed integer, or 64-bit signed integer, or
unlimited-size signed integer? If this is defined elsewhere, you
should have <a href="urlWhereDefined">integer</a> here.

What is the precise meaning of type 'char'? Is it US-ASCII 7-bit
character, or Latin-1 8-bit character, or UniCode-subset 16-bit
codepoint, or full UniCode 21-bit (embedded in 24-bit or 32-bit
machine word) codepoint, or what?? Ditto need href.

What is the precise meaning of type 'string', both in terms of
possible number of characters within a string, and what each
character is.

What is the precise meaning of type 'float'? Is it IEEE 754 single
precision, IEEE 754 double precision, IEEE 754 single-extended
precision, IEEE 754 double-extended precision, or some form of IEEE
854-1987, or any of those revised in 2008.Jun (this very month!!),
or something else?

] Variables with object types contain references to object values. This
] means that after
] a := b
] the variable 'a' refers to the same object as variable 'b'. Therefore
] changes on variable 'a' will effect variable 'b' as well (and vice
] versa) because both variables refer to the same object.

That is not worded well. There are two kinds of changes to variable
*a*, one which changes that variable itself to point to a different
object, and one which doesn't change *a* itself at all but instead
performs internal modification (what some other poster referred to
as "surgery", applied in his case to changing CAR or CDR of a CONS
cell, but the term could equally apply to *any* internal
modification of an object) upon whatever object *a* currently
points to.

If it's true that change in variable *a* itself by reassignment is
*not* passed to variable *b*, but "surgery" on the object that both
*a* and *b* point to *does* cause both *a* and *b* to "see" that
same change, you need to make that clear. Example of the distinction:
a := new tableWithLegs(color:purple);
b := a; /* *a* and *b* both point to same object */
tellIfLegs(a); /* Reports legs present, and purple */
tellIfLegs(b); /* Reports legs present, and purple */
paintLegs(a,color:green);
tellIfLegs(a); /* Reports legs present, and green */
tellIfLegs(b); /* Reports legs present, and green */
cutOffLegs(a); /* Legs of that single table are now without legs,
and henceforth the side-effect of that literal
surgery will be seen via both *a* and *b* */
tellIfLegs(a); /* Reports legs missing */
tellIfLegs(b); /* Reports legs missing */
a := new tableWithLegs; /* *a* and *b* now point to different tables */
tellIfLegs(a); /* Reports legs present, and beige (the default) */
tellIfLegs(b); /* Reports legs missing */
fastenNewLegs(b,color:orange);
tellIfLegs(a); /* Reports legs present, and beige */
tellIfLegs(b); /* Reports legs present, and orange */

] For primitive types a different logic is used. Variables with
] primitive types contain the value itself. This means that after
] a := b
] both variables are still distinct and changing one variable has no
] effect on the other.

This is correct, but totally confusing of the semantics I expressed
above are correct. If **assignment** of a new object to a variable
causes simultaneous assignment of all other variables that point to
the same object, a totally perverse semantics for your language,
which I ardently hope is *not* the case, then this all makes
(perverse) sense.

You seriously need to rewrite that whole section one way or another.

Suggestion (if I guessed the semantics correctly despite your
incorrect English): Say that in the case of primitive values, the
actual data, all of it, is right there in the variable itself, so
if you copy that value to somewhere else, and then change one bit
of one of the copies, the other copy won't be affected. But in the
case of Objects, what's in the variable is just a pointer to the
object, so you can't change bits in that pointer without trashing
the whole system (it now points to some random place in memory that
probably isn't an Object), so modifying Object variable isn't
allowed, so the question of what if you modify the actual value
itself doesn't make any sense. What you *can* do in the case of
Object variables is modify the actual Object it points to. Since
two different variables may point to the same object, that
modification (surgery) will be "seen" from both places equally.

Say that the other thing you can do with variables is to simply
re-assign the variable to have a new value, a new self-contained
value in the case of primitive variables, a pointer to a different
Object in the case of Object variables. In neither case is the
reassignment "seen" by any other variable that happened to share a
copy of the primitive value or pointer-to-Object. In the case of
primitive variables that previously contained copies of the exact
same primitive value, the two variables now have different
self-contained values, one of them now containing the
newly-assigned value, the other still containing the same original
self-contained value it had before. In the case of Object variables
that previously pointed to the same Object, they now point to
different Objects, one of them now pointing to the new Object that
was assigned to it, the other still pointing to the same Object it
already pointed to.

] In pure object oriented languages the effect of independent objects
] after the assignment is reached in a different way: Every change to an
] object creates a new object and therefore the time consuming copy
] takes place with every change.

I've never heard of any such language. Perhaps you can name one.
Java and Lisp in particular do *not* copy when objects are
modified. In Lisp you have the option, for some kinds of
intentional objects, such as sequences (linked lists and
one-dimensional arrays), to either optimize speed by destructively
modifying the object (what we're talking about here) or avoid side
effects by copying as much of the structure as needed (something
that *explicitly* says it returns a new object if necessary). (For
the no-side-effect version: For arrays you either make a complete
copy or you don't. For linked lists you usually keep/share the tail
of the original list past the last change, but re-build/copy
everything before that point.) I suspect your point here is strawman.

] ... In Seed7 every
] type has its own logic for the assignment where sometimes a value copy
] and sometimes a reference copy is the right thing to do. Exactly
] speaking there are many forms of assignment since every type can
] define its own assignment.

IMO this is a poor design decision. This means the same kind of
object, such as array, can't exist sometimes on the stack and
sometimes as an object in the heap, because the fact of how it's
allocated and copied is hardwired into the type definition. Better
would be to have a shallow-copy method for every object, and call
the shallow-copy object whenever the object is stored on the stack,
but simply copy the pointer whenever just the pointer is stored on
the stack. Thus it's the variable type (inline-stack-Object vs.
pointer-to-heap-Object), not the Object class, which determines
what copying happens. Then it would be possible to copy an object
from stack to heap or vice versa as needed. With your method, it
would seem that every instance of a given type of object must be on
the stack, or every instance in the heap, never some here and some
there as needed.

As for deep copy, that's a huge can of worms, telling the copy
operation when to stop and go no deeper. Kent Pittman discussed
this in his essay on intention. That's why copy-list copy-alist and
copy-tree all do different things when presented with exactly the
same *internal* datatype of CONS-tree. I think it's best if
assignment avoid this can of worms and let the programmer say
explicitly what kind of deep copy might ever be required in special
circumstances.

Robert Maas, http://tinyurl.com/uh3t

unread,
Jun 22, 2008, 7:54:18 PM6/22/08
to
> From: Your Name <n...@none.none>
> ... I don't really find OO very useful. ... the more complex the

> project, the more inheritance and information hiding become nothing
> more than a burden, in my experience.

Would you consider the following compromise: During development of
a package of software tools, everything except the widgets of the
GUI is done by ordinary Lisp-style functions, not OO. Macros are
defined only where essential to simplify the syntax to speed up
coding of lots of cases, but only after several cases have been
coded manually by ordinary function call with recursive evaluation
of nested expressions and explicit quoting of data that is not to
be evaluated. All the business logic is done by ordinary D/P
(Data-Processing) functions, possibly aided by macros. At the very
end, before releasing the code to regular users, an OO wrapper is
put around the public-accessible tools, limiting access/view of the
innerds.

> The open/closed principle

Which one??
<http://en.wikipedia.org/wiki/Open/closed_principle>
- Meyer's Open/Closed Principle -- Parent class remains unchanged
forever except for legitimate bug fixes. Derived classes inherit
everything that stays the same, and re-define (mask) anything
that would be changed compared to the parent class. The
interface is free to differ in a derived class compared to the
parent class.
- Polymorphic Open/Closed Principle -- A formal *interface* is set
up once, and then never changed. Various classes implement this
interface in different ways.

GUIs with classes of widgets that are interchangeable via events
triggered by mouse keyboard etc. satisfy the second definition.
Traditional modular programming (doesn't have to be per the current
jargon) ideally satisfies the first definition.

Common Lisp's keyword parameters allow a compromise (with the first
definition) whereby a function can be defined with limited
functionality, then a new keyword can be added to allow additional
functionality without changing the earlier functionality in any
way. In this way the primary value of Meyer's Open/Closed Principle
can be obtained without needing any actual OO.

> It encourages code to become set in stone.

Code for implementations, or code for interfaces?? Or both?

> Unfortunately, especially on a large project, I find that no
> matter how we try, it is impossible to foresee the entire scope
> of what needs to be designed.

Totally true, especially on "cutting edge" R&D which explores new
D/P algorithms and eventually settles on whatever works best (or
gives up if nothing works well enough to put into practice). I find
that most of my software is new-D/P-algorithm R&D where "agile
programming" methodology (without the overhead of purchasing a
commercial Agile (tm) software environment) is the only practical
course of action, and totally precludes the design-first
implement-last paradigm.

Refactoring is a daily experience as I try various algorithms until
I learn what works best. Deciding that I really *also* need to do
something I didn't even envision at the start of the project, in
addition to the fifth totally different algorithm for something I
*did* anticipate at the start, is a common occurance.

For example, in my current project for developing ProxHash to
organize a set of transferrale/soft skills (in regard to seeking
employment), I had no idea that there would be a very large number
of extreme outliers (records nearly orthogonal to every other
record) which would require special handling, until I had already
finished developing all the code leading up to that point where the
extreme outliers could be discovered. (If I had known this problem
at the start, I might have simply written a brute-force
outlier-finding algorithm, which directly compared all n*(n+1)/2
records, not scalable to larger datasets than the 289 records I'm
working with presently, but it sure would have simplified the rest
of the code for *this* dataset by having them out of the way at the
start.) If you're curious:
- Original data, with labels added:
<http://www.rawbw.com/~rem/NewPub/ProxHash/labsatz.txt>
- Identification of extreme outliers, starting with the *most* distant,
working down to semi-outliers:
<http://www.rawbw.com/~rem/NewPub/ProxHash/outliers-2008.6.11.txt>
Note that d=1.4142135=sqrt(2) means orthogonal, where correlation is 0.
The most distant outlier has d=1.39553 C=0.02625 to its nearest "neighbor".

> So the design is constantly changing, and the idea of some set of
> code that we write in the early stages surviving without needing
> vast rewrites for both functionality and efficiency is delusional.

Indeed, the "d" word is quite appropriate here. It's sad how
software programming classes so often teach the dogma that you must
spec out the entire project at the start before writing the first
line of code. Agile programming (including rapid prototyping,
bottom-up tool-building, and incessant refactoring in at three
different ways) is IMO the way to go.
<http://www.rawbw.com/~rem/HelloPlus/hellos.html#s4outl>


* Lesson 4: Refactoring syntax

* Lesson 5: Refactoring algorithms

(One other that came up in another thread, which I've now forgotten)
OK, I went back to my file of backup copies of articles posted, and
here's the relevant quote:


| After that point, my beginning lessons don't discuss additional
| ways of refactoring, but I think we would agree that further
| refactoring is *sometimes* beneficial:
| - Using OOP.
| - Using macros to allow variations on the usual syntax for calling functions.
| - Defining a whole new syntax for specialized problem domains,
| including a parser for such syntax.

Here's a reference for the entire article if you're curious:
<http://groups.google.com/group/comp.programming/msg/bc8967c3b7522c5d>
= Message-ID: <rem-2008...@yahoo.com>

> 1) The development tools and libraries are the most mature for
> C++ and this is essential.

I'm curious: Why did you choose C++ instead of Common Lisp?

> 2) The OS, drivers, and application code in the embedded firmware
> is all C and we have no choice in that, unless we want to develop a
> compiler for some other language, or write directly in assembly
> language.

I agree with that choice. C is fine for writing device drivers.

> Since C and C++ are largely similar, parts of the C code that we
> want to share with the other applications can be used directly.

Why would there be any code shared between device drivers and other
parts of your application???

> But if not for these two restrictions, in hindsight, I would not
> have chosen C++ or any other OO language.

If there was in fact no significant amount of code shared between
device drivers and the rest of the application, then maybe chosing
C++ was a mistake anyway? I'll withhold judgement until I know the
answer the shared-code-dd/app question just above.

> I've wandered far enough off-topic from the original post.

That's not a problem at all, since we're still totally on-topic for
the newsgroup comp.programming, and even for comp.software-eng in
case anybody wants to cross-post parts of this thread there.

Bartc

unread,
Jun 23, 2008, 9:00:51 AM6/23/08
to

"Robert Maas, http://tinyurl.com/uh3t"
<jaycx2.3....@spamgourmet.com.remove> wrote in message
news:rem-2008...@yahoo.com...

<snip pro-Lisp anti-everything else stuff>

> Only in the really stupid cruddy languages such as C you're
> familiar with.

What would your language of choice be for implementing Lisp?

--
Bartc


Robert Maas, http://tinyurl.com/uh3t

unread,
Jun 23, 2008, 9:39:32 PM6/23/08
to
> > Only in the really stupid cruddy languages such as C you're
> > familiar with.
> From: "Bartc" <b...@freeuk.com>

> What would your language of choice be for implementing Lisp?

A bootstrapping/crosscompiling process involving extended versions
of SYSLISP (which was available in PSL) and LAP (which was
available in MacLisp and Stanford Lisp 1.6). Revive those
dinosaurs, and enhance them to support generating native
executables for bootstrapping purposes.

When I helped port the PSL kernel from Tenex to VM/CMS, we needed
to write the outer frame of the executable in IBM 360/370 assembly
language, because SYSLISP required the IBM 370/370 registers (base
register and stack mostly) to be already set up before code
generated by SYSLISP would execute propertly. It seems to me
entirely reasonable to enhance SYSLISP or LAP to be able to
generate those first few instructions that reserve memory for the
stack and load the registers needed by the regular code.

So the basic plan would be as follows:
- Read the internal documentation for the first target system, to
learn what the format of executable files is supposed to be.
Write code in an earlier version of Lisp (anything that's
currently available, even XLISP on Macintosh System 7.5.5 might
be "good enough" for this purpose) to generate the minimal
executable file and to have a hook for including additional code
in it. A sequence of (write-byte whatever outputStream)
statements would be good enough to directly generate the minimal
executable file. Or LAP could be fixed to allow directly
generating inline code, not wrapped in any FUNCTION header, and
to call WRITE-BYTE instead of building the body of a FUNCTION.
Or LAP could build a dummy function body and then the body could
be copied to the outputChannel and then the dummy function
discarded. Or use something totally esoteric, instead of Lisp,
to implement something like LAP, such as Pocket Forth, or
HyperCard/HyperTalk, or an assembler. But actually the advantage
of implementing LAP to do the right thing is that the code for
that can then be ported to later stages in the bootstrapping
process to avoid needing to re-do all that work in Lisp later
when it's time to "close the loop". On the other hand, writing a
Forth emulator in Lisp would be easy enough, so if Pocket Forth
is used at this stage none of the Forth code would need to be
discarded, it could be kept as part of the finished product.
But actually using real genuine LAP syntax instead of something
easy for Forth to parse would be best, so I retreat to using
some earlier version of Lisp to implement the revitalized LAP.
In any case, LAP doesn't need to actually know any opcodes. It's
sufficient to know the different formats of machine language
instructions and accept hexadecimal notation for opcode and each
other field within each machine format instruction, or possibly
just accept a list whose first element is a symbol identifying
the rest of the list as hexadecimal bytes and the rest of the
elements handcoded byte of the instruction.
- Hand-code in assembly language, using hexadecimal-LAP syntax, the
absolute minimum application to assemble code, taking input from
a file IN.LAP in LAP syntax and writing to a file OUT.EXE in
target-machine language. Add that code to the minimal executable
from above and pass all that code through the earlier-version-of-Lisp
LAP above, thereby generating an executable that all by itself
assembles LAP. The earlier-LISP can now be despensed with since
we now have an assembler which can assemble itself.
- Hand-code in assembly language, using LAP syntax, enhancements to
the LAP assembler, such as the full set of instruction formats
(still with all hexadecimal fields), opcodes for the target
machine that know what instruction format is used by each,
labels that can be referred to from other places in the code.
After each round of this enhancement is completed, the next
round will be easier to code. After labels are implemented,
*real* assembly-language programming is possible, with lots of
subroutines used for nice structured programming.
- Hand-code in assembly language, using full LAP syntax from above,
the transformations needed to map a minimal subset of SYSLISP to
LAP. Now we have a compiler that takes a mix of SYSLISP-subset
and LAP and produces an executable.
- Code in SYSLISP-subset all the enhancements needed to implement
the full SYSLISP syntax and semantics. Now we have a compiler
that takes a mix of SYSLISP and LAP and produces an executable.
- Code in SYSLISP the bare minimum primitives needed to build a
symbol (in a default package that isn't a fullfledged PACKAGE
object in the usual sense) and access key fields from it, box
and unbox a small integer, build a CONS cell and access CAR and
CDR from it, parse a simple s-expression containing only symbols
and small integers to produce a linked list of CONS cells etc.,
print out such an s-expression from the linked list, enhance
SYSLISP to support wrapping a function body around a block of
code and linking from a symbol to that function body, applying
such a function to a list of parameters, EVALing small integers
to themselves and symbols to their current values, and
recursively EVALing a linked list whose CAR is the symbol of a
defined function and whose CDR is a list of expressions that
evaluate to parameters to that function. Include all that code
in compilation to our executable. We now have a mimimal LISP-subset1
interpreter that automatically builds an executable each time it
is run.
- Rearrange the code so that there's a function called
START-WRITING-EXECUTABLE which opens OUT.EXE for output and
generates the starting data for an executable, a function
called COMPILE-LAP+SYSLISP which takes a linked-list of a mix of
LAP and SYSLISP and compiles them into the already-open
executable-output file, a function called
FINISH-WRITING-EXECUTABLE which writes any necessary finalization
data in the executable and closes the output file. We now have
an interpretor we can just use as we want without necessarily
writing an executable, but any time we want we can code
(START-WRITING-EXECUTABLE)
(COMPILE-LAP+SYSLISP ...) ;More than one such can be done here
(FINISH-WRITING-EXECUTABLE)
.. details of additional levels of bootstrapping not included here ...

Now when we want to port everytning up to some bootstrapping point
to another machine, all we need is some crude way to establish the
very first bootstrap on the new machine, then copy all the various
additional bootstrapping code across from the old machine to the
new machine and manually replace the specific machine instructions
that aren't the same between old and new CPUs and then run the
result through the current level of bootstrapping to build the
executable for the next level of bootstrapping. By the time we
reach the level of having a minimal SYSLISP compiler, virtually
nothing more would need translation to the new CPU because SYSLISP
is mostly portable. During really high-level bootstrapping, only
the parts that need to be written in LAP, such as tag bits on
various kinds of data objects, and system calls to allocate memory
or do filesystem operations, would need to be manually converted to
a new target machine. But once we have a halfway decent Lisp
interpretor working, it's easy to write code to maintain a set of
tables that formally express tag bits and stuff like that, and then
generate LAP expressions dependent on those tables, such that each
machine-level characteristic would need to be coded in the tables
just once then all the LAP related to it could be generated
automatically.

Of course you could *cheat* by using an existing fullfledged
implementation of Common Lisp, which *was* coded in C, to implement
the entire LAP and SYSLISP compiler as functions within that
existing implementation, and then build more and more of the *new*
Lisp implementation by generating the executable directly from that
existing+LAP+SYSLISP environment. That would let you shortcut some
of the early bootstrapping levels. But that's not as much fun, and
really it is cheating to use anything that passed through C for
your cross-compiler, and it really would be *cheating*.

Now to *really* avoid all sense of using C, you can't implement
your first bootstrap using any operating system that was even
partly built using C, especially not Unix. I think early versions
of MacOS (at least through 6.0.7) were built using Pascal rather
than C, is that correct? But for the full spirit of this avoidance
of using some *other* programming language to cross-compile into
Lisp, thereby making Lisp dependent on that *other* cursed
language, we really ought to avoid *any* other language above
assembly language. My MOS-6502 bare machine, bootable using pROM
Octal-DDT, is in storage but was working the last time I tried it.
It could perhaps be used to avoid both C and Pascal as well as any
other high-level language. Does anybody have a PDP-10 that still
works? Its operating system was written in assembly language, the
original DEC systems using Macro, and the Stanford-AI system using
FAIL. What was MIT's ITS written in, DDT?

gremnebulin

unread,
Jun 24, 2008, 6:11:51 AM6/24/08
to
On 9 Jun, 19:46, Richard Heathfield <r...@see.sig.invalid> wrote:

> > The process, for lack of a better term, is "compression."
>
> Or abstraction, or functional decomposition...

Or chunking, or convergence...

Bartc

unread,
Jun 24, 2008, 6:34:43 AM6/24/08
to

"Robert Maas, http://tinyurl.com/uh3t"
<jaycx2.3....@spamgourmet.com.remove> wrote in message
news:rem-2008...@yahoo.com...
>> > Only in the really stupid cruddy languages such as C you're
>> > familiar with.
>> From: "Bartc" <b...@freeuk.com>
>> What would your language of choice be for implementing Lisp?
>
> A bootstrapping/crosscompiling process involving extended versions
...

> Of course you could *cheat* by using an existing fullfledged
> implementation of Common Lisp, which *was* coded in C, to implement
> the entire LAP and SYSLISP compiler as functions within that
> existing implementation, and then build more and more of the *new*
> Lisp implementation by generating the executable directly from that
> existing+LAP+SYSLISP environment. That would let you shortcut some
> of the early bootstrapping levels. But that's not as much fun, and
> really it is cheating to use anything that passed through C for
> your cross-compiler, and it really would be *cheating*.

OK, so you're going out of your way to avoid using C, for reasons I don't
fully understand (or perhaps I do :-)

Although I would just have used assembler, if conventional HLLs were out;
Lisp doesn't look that difficult to implement, at first glance...

> Does anybody have a PDP-10 that still
> works?

alt.sys.pdp10

Apparently one just got sold on eBay.

--
bartc


thomas...@gmx.at

unread,
Jun 26, 2008, 5:21:31 PM6/26/08
to
On 23 Jun., 00:05, jaycx2.3.calrob...@spamgourmet.com.remove (Robert

Maas, http://tinyurl.com/uh3t) wrote:
> > From: thomas.mer...@gmx.at
> > If you want to invent your own abstraction mechanisms you might
> > be interested in Seed7. There are several constructs where the
> > syntax and semantic can be defined in Seed7:
> > - Statements and operators (while, for, +, rem, mdiv, ... )
>
> Some of those are such commonly useful constructs that it seems
> ill-conceived to have each application programmer re-invent the
> wheels.
[snip]
Thank you four your feedback. Since I am still evaluating your mail
I do not respond to all your questions. I tried to improve the FAQ
and the manual to include some answers to your questions. To keep
the answer short I suggest you read some links:

> Are there standard packages available to provide these as
> "givens" with a well-documented API so that different application
> programmers can read each other's code?

It is not the intention of Seed7 that everybody re-invents the
wheel. There is a well-documented API. The predefined statements of
Seed7 are described here:
http://seed7.sourceforge.net/manual/stats.htm

Other types and their functions (methods) are described here:
http://seed7.sourceforge.net/manual/types.htm

> Again, these are such basic container types that they really ought
> to be provided in a standard package. Are they?

A short explanation of Seed7 container types is here:
http://seed7.sourceforge.net/faq.htm#container_classes

Details to some abstract data types cen be found here:
http://seed7.sourceforge.net/manual/types.htm#array
http://seed7.sourceforge.net/manual/types.htm#hash
http://seed7.sourceforge.net/manual/types.htm#set
http://seed7.sourceforge.net/manual/types.htm#struct

> Um, C is not exactly a high-level language.

What computer scientists mean when they speak about "high level
languages" is explained here:
http://en.wikipedia.org/wiki/High_level_language

> Ugh! So you're using C almost as if it were an assembly language,

The first C++ compiler did this also: Cfront generated C output
which was compiled with a C compiler. See:
http://en.wikipedia.org/wiki/Cfront

> What precisely do you mean by "templates"?

What computer scientists mean when they speak about "templates"
is explained here:
http://en.wikipedia.org/wiki/Template_%28programming%29

> What precisely do you mean by "generics"?

What computer scientists mean when they speak about "generics"
is explained here:
http://en.wikipedia.org/wiki/Generic_programming

> Why make a distinction between statements and expressions-with-operators??

Because most people see operators special. Probably because they
think of infix operators with priority and associativity. Languages
like Ada, Java, C++ also see them as destinct. In Seed7 there is no
such destinction.

> There's hardly any value to static type checking, compared to
> dynamic (runtime) type-checking/dispatching.

Just because a feature like mandatory static type checking is not
present in your favorite language (Lisp) does not turn this concept
into something negative. To learn something about type systems I
suggest you read:
http://en.wikipedia.org/wiki/Type_system

Type checking and dispatching are different things. Although Seed7
does static type checking you can choose between compile-time and
run-time dispatching. The OO concept of Seed7 is explained here:
http://seed7.sourceforge.net/manual/objects.htm

I have also improved my FAQ about static type checking. See:
http://seed7.sourceforge.net/faq.htm#static_type_checking

The difference between your "intentional datatype" and your
"internal datatype" lies in the measurement units. A static type
system as Seed7's can also be used to cope with measurement units.

> Does it run on FreeBSD Unix? (That's what my shell account is on.)

If a shell, a make utility and a C compiler with header files and
library are available I see no reason that compilation should not
work. At least I had reports of people who successfully compiled
Seed7 on FreeBSD.

> Why doesn't it run on MacOS 6 or 7?

Where did you get the impression that it does not run on MacOS?
I have not tried it, but that does not mean that is is not possible.
One thing necessary to support Seed7 is a (real) C compiler and
a C library.

> ] * The possibility to declare new statements (syntactical and
> ] semantically) in the same way as functions are declared
>
> Are you really really sure that's a good idea.

Yes

> - It makes one person's code unreadable by anyone else.

I have added an answer to this question to the FAQ. See:
http://seed7.sourceforge.net/faq.htm#everybody_invents_statements

> Please reconsider your decision to use operators...
> Please consider going back to square one...

You obviously have no idea for how long I am already working on the
concepts of Seed7. Seed7 is not Lisp, nor wants it to be. The S7SSD
(the Seed7 structured syntax description) is one of the basic
building blocks of Seed7 (a chapter about it will be added to the
manual in the near future). Seed7 is about the possibility to
define new syntax and semantic. Therefore it is possible to steal
constructs from other languages. Additionally the syntax and the
semantic of the predefined constructs is defined with much more
precision since they are defined in terms of Seed7.

> Some/most/all of the other features you claim aren't available in
> other languages are in fact already available in Common Lisp.

Not exactly. See:
http://seed7.sourceforge.net/faq.htm#lisp_comparison

> ] Although functions can return arbitrary complex values (e.g. arrays of
> ] structures with string elements) the memory allocated for all
> ] intermediate results is freed automatically without the help of a
> ] garbage collector.
>
> How?????

I have improved the FAQ for this. See:
http://seed7.sourceforge.net/faq.htm#garbage_collection

> Debug-use case: A application is started. A sematic error (file
> missing for example) throws user into break package. User fixes the
> problem, but saves a pointer to some structure in a global for
> later study.

You are much too deep in the Lisp way of thinking. If a stack
shrinks the elements popped from the top just don't exist any more.
It will lead to undefined behaviour if you keep pointers to such
nonexisting elements and expect the data to be still there. It is
also a common bug in C/C++ and other similar languages when a
function returns a pointer to some local data (which is at the
stack): It is a severe bug (although it may or may not work
depending on the implementaion). If you want such data to be
available later you need to make a (deep) copy.

This is also the reason why Seed7 functions use a 'return' variable
to refer to the result of a function. The 'return' variable is
handled specially such that no (deep) copy is necessary when the
function is left.

> ] type save solution for containers ...
> **f* (typo, the first typo I've found so-far, your English is good!)

Thank you for pointing this typo out. Since I am not a native
speaker it is hard to get the spelling right.

> But having *any* runtime dispatching based on actual type of an
> object defeats your <coughCough>wonderful</coughCough> static type

> checking.

No. The interface types can be checked statically and it is also
possible to check the implementation type that it implements all
functions granted by the interface. The terms interface and
implementation and the OO concept of Seed7 is explained here:
http://seed7.sourceforge.net/manual/objects.htm

> ;Static type checking fails to detect this type-mismatch undefined-method error.

You obviously did not understand that static type checking is used
to reduce the possibility of bugs, not to make them impossible.
Every bug found at compile-time will not make you trouble at
run-time. The earlier you can eliminate bugs the better.

> What is the precise meaning of type 'integer'?

Integers are at least 32 bit signed integers.
The type 'integer' is explained here:
http://seed7.sourceforge.net/manual/types.htm#integer

There is also the type 'bigInteger' which serves as unlimited-size
signed integer. The type 'bigInteger' is explained here:
http://seed7.sourceforge.net/manual/types.htm#bigInteger

> What is the precise meaning of type 'char'?

The 'char' values use the UTF-32 encoding, see:
http://seed7.sourceforge.net/manual/types.htm#char

> What is the precise meaning of type 'string', both in terms of
> possible number of characters within a string, and what each
> character is.

The characters in the 'string' use the UTF-32 encoding. The Seed7
strings are not \0 terminated and therefore can also contain binary
data. The length of the string is stored in a 32 bit unsigned
integer. The type 'string' is explained here:
http://seed7.sourceforge.net/manual/types.htm#string

> What is the precise meaning of type 'float'?

The type 'float' is IEEE 754 single precision (at least when the C
compiler/library uses it). The type 'float' is described here:
http://seed7.sourceforge.net/manual/types.htm#char

> ] Variables with object types contain references to object values. This
> ] means that after
> ] a := b
> ] the variable 'a' refers to the same object as variable 'b'. Therefore
> ] changes on variable 'a' will effect variable 'b' as well (and vice
> ] versa) because both variables refer to the same object.
>
> That is not worded well.

I have changed it to

Variables with object types contain references to object values.
This means that after
a := b
the variable 'a' refers to the same object as variable 'b'.

Therefore changes of the object value that 'a' refers to, will


effect variable 'b' as well (and vice versa) because both
variables refer to the same object.

> ] In pure object oriented languages the effect of independent objects


> ] after the assignment is reached in a different way: Every change to an
> ] object creates a new object and therefore the time consuming copy
> ] takes place with every change.
>
> I've never heard of any such language.

This concept is called immutable object. It is explained here.
http://en.wikipedia.org/wiki/Immutable_object

This immutable objects concept is not used always and for all
objects. AFAIK Java and C# use immutable strings. If you concatenate
a lot of elements to a string the immutable strings are copied a
lot. This is the reason that Java introduced the mutable strings
classes StringBuffer and StringBuilder.

> ] ... In Seed7 every
> ] type has its own logic for the assignment where sometimes a value copy
> ] and sometimes a reference copy is the right thing to do. Exactly
> ] speaking there are many forms of assignment since every type can
> ] define its own assignment.
>
> IMO this is a poor design decision.

No. If you assign an integer you want a deep copy, but if you
assign a file you want to copy just the reference to it.
If you define a data structure where at some place no deep
copy should take place you can use pointers instead of
a normal type.

> This means the same kind of
> object, such as array, can't exist sometimes on the stack and
> sometimes as an object in the heap, because the fact of how it's
> allocated and copied is hardwired into the type definition.

Allocation of memory for an object, assigning a value to an object
for the first time (with the Seed7 'create' operation) and copying
an object (with the Seed7 := operator) are different things.

Seed7 Arrays can change their size at runtime therefore it would
not be easy to put them on the stack.

Greetings Thomas Mertes

Seed7 Homepage: http://seed7.sourceforge.net

Robert Maas, http://tinyurl.com/uh3t

unread,
Jul 5, 2008, 11:04:47 AM7/5/08
to
> Date: Sun, 22 Jun 2008 13:13:13 +0100
Why this response is so belated:
<http://groups.google.com/group/misc.misc/msg/cea714440e591dd2>
= <news:rem-2008...@yahoo.com>
> From: Jon Harrop <j...@ffconsultancy.com>

> Haskell, SML, OCaml, Mathematica, F# and Scala all allow real
> problems to be solved much more concisely than with Lisp. Indeed, I
> think it is difficult to imagine even a single example where Lisp
> is competitively concise.

What does "solved much more concisely" mean??

The solution to any problem of significant complexity requires a
lot of code. Some of the code is in the core language, some is in
the libraries that ship with the core language, some is in
third-party libraries or vendor options, and some needs to be
written because that particular aspect of the problem has never
been solved before and there's always a first time for anything
that gets done. When you include *all* that code, there's no way
the totality of all that can be "concise".

Each language makes a design choice which kinds of tools are likely
to be generally useful and hence ought to be included in the core
langauge or supplied libraries, and everything else is left for
later (add-ons from vendor, third-party, and do-it-yourself).

But regardless of how much of the code is supplied by the vendor
and how much is supplied by third parties, in the end when all the
code is written, and it's time to actually run an application, the
command to run it is typically very short: You give the name of the
utility, you give a pointer to the file containing the
configuration parameters, you give a pointer to the file containing
the dataset (or that pointer is contained within the configuration
file), and you press ENTER or click the SUBMIT button to confirm
you want that task to actually execute.

So why don't you say what you really mean, that some particular
application types are better represented in published libraries in
your favorite language than in mine (Lisp)? And people who like
Lisp will respond that some *other* application types are better
represented in published libraries in Lisp language than in
*yours*? Why don't you compile a list of all the major application
types that you feel are needed by the largest number of potential
customers/users, and allow the rest of us to propose additions to
your list? For example, IMO the most commonly useful application
types are:
- Business operations (accounts receivable/payable, inventory, payroll)
- Text editing (both plain text and "word processing" formatted text)
- Web browsing/surfing/searching
- E-mail (both plain text and MIME multi-media) including protection against spam
- Conferencing and other public/group forums
- Security, including protection against computer viruses/worms/trojans/bots
There are probably five or ten more that I could add if I had the
time/energy to think of them. Anyway, after we get a complete list,
based on concensus of everyone reading comp.programming on a
regular basis and seeing this thread, *then* you need to say how
well your favorite language addresses these common application
needs.

Note that algebraic datatypes are not among the major applications I listed.

> Consider the trivial example of defining a curried "quadratic"
> function. In Common Lisp:
> (defun quadratic (a) (lambda (b) (lambda (c) (lambda (x)
> (+ (* a x x) (* b x) c)))))

I've never in my entire life found an application area where such a
tool was needed or even useful. Can you please cite what real-world
problem was aided by currying a quadradic or other function? If
it's a rare problem, then the two lines of code above are entirely
concise enough. I anybody needed to do a *lot* of that apparent
pattern of nested lambdas with one parameter each, it would be
simple to write a macro whereupon the syntax for actual usage would
reduce to:
(defcurry quadratic (a) (b c x) (+ (* a x x) (* b x) c))
Actually the expression for the quadradic is cruddy: Imagine trying
to extend that to a high-degree polynomial:
(defcurry quintic (a) (b c d e x)
(+ (* a x x x x x) (* b x x x x) (* c x x x) (* d x x) (* e x x) f))
Wouldn't it make more sense to chain the multiplications like this:
(defcurry quadratic (a) (b c x)
(+ c (* x (+ b (* x (+ a))))))
(defcurry quintic (a) (b c d e x)
(+ f (* x (+ e (* x (+ d (* x (+ c (* x (+ b (* x (+ a))))))))))))
Or for that matter, if all you're generating are standard
polynomials then you can abstract the pattern further:
(defcurrypoly quadratic (x) (a b c))
(defcurrypoly quintic (x) (a b c d e f))

> In F#:
> let inline quadratic a b c x = a*x*x + b*x + c

That's more verbose than my use of defcurrypoly.
Does F# provide a way to carry out the abstraction of defcurrypoly?
Also, it's absurd that the keyword for generating a curried
function is "inline" instead of "curry". I know that "curry" is
CompSci jargon, but at least it's not grossly misleading as to what
it does the way the "inline" keyword in F# is!! Anybody who doesn't
understand the jargon can look it up in Google or WikiPedia and
learn what the jargon means and not be confused. By comparison,
anybody reading code that mentions "inline" would be very unlikely
to guess that he/she should read that as what "curry" means.
(Of course if you're lying about what the "inline" keyword does in
F# then the fault lies with you, not with F#.)

By the way, sometimes the biggest gripe about mis-named functions
in Lisp is CONS CAR CDR. Like CONS is an abbreviation for
"construct", which begs the question *what* precisely is being
constructed, and CAR/CDR are totally obscure. IMO better names for
the object itself would be Standard Pair, abbreviated SP, and then
the three functions could be named MAKE-SP SP-LEFT SP-RIGHT and be
totally clear sensible to newbies. (The name for the print
expression for Standard Pair could remain "Dotted Pair", and the
connection between the internal Standard Pair and the printed
Dotted Pair would make a lot more sense to beginners.)
Note there are a lot of different kinds of (ordered) pairs available in Lisp:
- Pair of characters in a string, such as that which prints as "az"
- Pair of elements in a linked list, such as that which prints as (1 2)
- Pair of elements in a vector, such as that which prints as #(1 2)
- Key-value pair in a hash table, which has no print form but it's
the result of something like (setf (gethash :SURNAME *ht*) "Robert")
It's just that the kind of ordered pair constructed by CONS is the
*standard* kind that is essential to Lisp itself. Maybe *basic*
instead of *standard* would be a better term, but then the asswipe
who invented BASIC might sue for infringement. Maybe *common* would
be a better term? (Play on first word of "Common Lisp")

> > Suppose you want to hand-code a list
> > of data to be processed, and then map some function down that list.
> > In Lisp all you need to do is
> > (mapcar #'function '(val1 val2 val3 ... val4))
> > where the vals are the expressions of the data you want processed
> > and function is whatever function you want applied to each element
> > of the list. You just enter that into the REP and you're done.
> Lisp:
> (mapcar #'function '(val1 val2 val3 ... val4))
> OCaml and F#:
> map f [v1; v2; v3; .... vn]
> Mathematica:
> f /@ {v1, v2, v3, ..., vn}

OK, for simple lists those three languages equal Lisp in conciseness.
So what if you want to pass a more complicated structure to some function.
For example, how do you pass a nested ASSOC list to a function that
automatically traverses it matching keys against given keys to find
the correct branch at each level? Suppose that the function is
called fks which is an abbreviation for Find Key Sequence (in such
a nested assoc list implied).
(defparameter *nal1*
'(:TELEPHONECODES
(:USA "1" (:OREGON "503" "541" "971") (:NEVADA "702" "775")
(:NORTHDAKOTA "701"))
(:CANADA "1" (:MANATOBA 204))
(:BELGIUM "32" (:ANTWERP "3") (:BRUSSELS "2"))
(:CROATIA "385" (:DUBROVNIK "20") (:ZAGREV "1"))
))
(fks '(:TELEPHONECODES :CROATIA :DUBROVNIK) *nal1*) => ("385" "20")
(fks '(:FOOBAR :USA)) ;signals error, toplevel tag wrong
(fks '(:TELEPHONECODES :MEXICO :CANCUN) ;signals error, inner tag #1 missing
(fks '(:TELEPHONECODES :USA :TEXAS) ;signals error, inner tag #2 missing

Would that be equally simple to directly code in those three
languages you suggest?

> ... modern functional languages like Haskell, OCaml and F# ...
> They [users] clearly do not have a problem with the supreme brevity
> of these languages.

In cases where those particular languages are not already brief as
delivered, how easy is it to define a shorter syntax if you're
going to want a particular operation very terse because it's going
to be done a lot? I.e. how do they compare with Lisp's ability to
define macros?

> ... I was listing some of Lisp's most practically-important


> deficiencies. Lack of static typing in the language is one.

I disagree. I pointed out how static typing doesn't really solve
the problem at all because it's incapable of dealing with
intentional data types which is where the *real* problem lies.
Static type-checking of internal datatypes doesn't hardly begin to
solve the real problem. It's just an advertising gimmick that turns
out to be more cost than value. (The exception is in tight loops
where some machine type happens to work efficiently and where that
particular loop eats up most of the CPU time, which is precisely
where Lisp allows type declarations and consequent CPU-efficient
code as an *option* where it would actually be worth the cost.)

> Lack of concurrent GC in all Lisp implementations is another.

I gotta look up that to see what the term means ... first match is:
<http://xiao-feng.blogspot.com/2007/03/about-mostly-concurrent-gc.html>
That gives me some idea, but it doesn't seem like what you desire, right?

Skipping a few search results that don't look worth studying, I come to:
<http://groups.google.com/group/comp.lang.functional/browse_thread/thread/a288a989c5cd6acb/50b6ca2607173c91>
"So concurrent GC makes communication cheaper at the expense of everything else?"
That was written by none other than Jon D Harrop!
(The point was that if you have a shared mutable object, then you
need concurrent GC, but usually it's better design to have each
thread have its own mutable data and instead marshall data to pass
back and forth as needed, but then passing large objects back and
forth can cost a lot.)
There's a compromise between shared mutable data and passing large
data objects back and forth, which is to have each mutable data
object owned by just one process and everyone else must mutate or
examine it via queries in the OO style. So then only the handle on
the object, the parameters of the query (which may be handles on
other objects), and the return value (some piece of the object, or
a success/fail flag) need be passed between processes. It seems to
me that having an actual shared-mutable-object is almost as bad as
having a global variable that is mutable by multiple processes
(except for semaphores/interlocks/etc. which really *do* need to be
shared at a low level to make multi-processing work in the first place).
Later in that thread, it's pointed out that sharing memory doesn't
scale to really large applications, so you need to marshall data
(even if it's only ints) to pass between distant processors.
So in essence shared memory is a bad idea in large applications
where you need parallization in the first place, so you should
avoid shared memory, and thus there's no need for concurrent GC.
Later in that same thread, Harrop said "I'm hard pressed to find a
practically-important application that will be better off with a
concurrent GC." So if concurrent GC is essentially worthless, why
is Lisp's lack of it a reason for disliking Lisp??

<http://portal.acm.org/citation.cfm?id=1345206.1345238&coll=GUIDE&dl=GUIDE>
Hmm, this paper claims that "concurrent GC can share some of the
mechanisms required for transactional memory" thus as mechanisms
for the latter are made more efficient, same applies to former. But
I don't have time to look up yet another jargon term at bedtime to
know what the author is talking about.

So anyway, whether concurrent GC is worth the trouble or not, is debatable.

> Also lack of threads,

We're in agreement that the ANSI standard didn't include threads,
which is a major deficiency if you look to fully portable software
between different implementations of CL. Allegedly at that time the
Lisp community didn't have enough experience with threads to be
sure what version of them ought to be standardized. IMO the best
strategy is to keep the code specific to thread management totally
disjoint from the business logic so that thread management can be
simply replaced when porting to a different implementation.

> weak references,
[suffix "are all fundamental deficiencies of the language" for
these next several items]

It seems to me that a GC hook could be used to purge transient data
that hasn't been referenced recently. How easy is it to actually
implement this and get it working well with only ANSI CL to work
with?

> finalizers,

You mean like to automatically close a system resource whenever the
Lisp hook for it is GC'd? Normally system resources like that are
lexically bound, whereby UNWIND-PROTECT is sufficient to make sure
they get closed when that block of code is exited. Is there
*really* a good reason to have such resources kept around per data
object instead of per block-entry/exit? Somehow it seems to me a
dangerous design to have system resources that get closed at
"random" times (when memory runs short) instead of at clearly
defined times. But if you can think of a really good use for random
closure, I may reconsider my opinion.

> asychronous computations,

I'm not sure what you mean. Don't compilers already generate
efficient CPU data+instruction parallel-pipelining whenever they
detect that two operations are done in close proximity neither of
which depends on the result of the other?

> memory overflow recovery,

That might be moot on typical modern architectures that have a
small CPU cache and a large RAM and a truly gigantic virtual
address space. Locality of reference to avoid thrashing is a
problem long before total virtual memory or disk space for swapping
is ever exceeded.

> > Please write up a Web page that explains:
> > - What you precisely mean by "concurrent GC" (or find a Web page
> > that somebody else wrote, such as on WikiPedia, that says
> > exactly the same as what *you* mean, and provide the URL plus a
> > brief summary or excerpt of what that other Web page says).
> See Jones and Lins "Garbage Collection: algorithms for automatic dynamic
> memory management" chapter 8.

I don't have access to that. Is there a summary on the Web?

> In a serial GC, the program often (e.g. at every backward branch)
> calls into the GC to have some collection done. Collections are
> typically done in small pieces (incrementally) to facilitate
> soft-real time applications but can only use a single core. For
> example, OCaml has a serial GC so OCaml programs wishing to use
> multiple cores fork multiple processes and communicate between them
> using message passing which is two orders of magnitude slower than
> necessary, largely because it incurs huge amounts of copying that
> is not necessary on a shared memory machine:

On a shared-memory machine, how difficult is it to prevent
different processes from trying to read and write the same object
at the same time, whereupon the writer has only partly overwritten
the object when the reader starts to read it and sees pieces of old
and new object scrambled together?

> With a concurrent GC, the garbage collector's threads run
> concurrently with the program threads without globally suspending
> all program threads during collection. This is scalable and can be
> efficient but it is incredibly difficult to implement correctly.
> The OCaml team spent a decade trying to implement a concurrent GC
> and never managed to get it working, let alone efficient.

Maybe it's just too tricky (to implement correctly) to be worth
doing? Maybe the Lisp implementors were wise not to make that
gamble? Maybe it's "not ready for prime time", i.e. until somebody
finds a way to get it right, it's not worth fretting over the fact
that it hasn't been done, it's better to just live without it?

> > - List several kind of applications and/or API tools that are
> > hampered by lack of whatever that means.
> Any software that requires fine-grained parallelism for
> performance will be hampered by the lack of a concurrent GC.

List five such applications, and convince me that any technique
other than fine-grained parallelism is too inefficient for each
such application. Also convince me in each case that circular
structure is absolutely necessary, because after all if there are
no pointer loops then reference counting works just fine and no GC
is needed at all. Convice me that a hybrid system, using a symbol
table of tops of structures, with no loops within any of the
structures except that symbols in the symbol table may be
mentionned within structures, would not be sufficient for each such
application.

> >> 2. Even though Lisp's forte is as a language laboratory, Lisp has
> >> so little to offer but costs so much
> > Um, some implementations of Common Lisp are **free** to download
> > and then "use to your heart's content". How does that cost too much???
> Development costs are astronomical in Lisp compared to modern
> alternatives like F#, largely because it lacks a static type system

As I pointed out before, static type-checking doesn't solve enough
of the "problem" to be worth the trouble it causes. It doesn't
(can't) handle intentional types. Any language that imposes static
typing is what slows down development.

> but also because it lacks language features like pattern matching,

Here we go again.

> decent developer tools like IDEs

I used to use Macintosh Allegro Common Lisp on my Mac Plus.
Wasn't that a IDE?

> There are no decent Lisp implementations for .NET.

Why does anybody care? What, if anything, is good about .NET?

> Finally, there is no way you'll ever turn a profit because the
> market for commercial third-party software for Lisp is too small.

Consider leased server-side applications (CGI or PHP). How would
the customer know or care whether Lisp or some other language
happens to be used to implement it? Just about everyone on the net
uses the Google search engine. It's advertising based rather than
subscription, but ignore that detail for a moment. Do you know what
language(s) are used to implement all its internals? Do you care?

> I think you should aspire to earn money directly from customers
> rather than asking people to give you money to develop your
> software.

I like that idea very much. Will you find me a potential customer
who wants some serverside application that doesn't already exist?

Robert Maas, http://tinyurl.com/uh3t

unread,
Jul 5, 2008, 11:23:55 PM7/5/08
to
> Date: Tue, 24 Jun 2008 10:34:43 GMT
> From: "Bartc" <b...@freeuk.com>
> >> What would your language of choice be for implementing Lisp?
> > A bootstrapping/crosscompiling process involving extended versions
> ...
> > Of course you could *cheat* by using an existing fullfledged
> > implementation of Common Lisp, which *was* coded in C, to implement
> > the entire LAP and SYSLISP compiler as functions within that
> > existing implementation, and then build more and more of the *new*
> > Lisp implementation by generating the executable directly from that
> > existing+LAP+SYSLISP environment. That would let you shortcut some
> > of the early bootstrapping levels. But that's not as much fun, and
> > really it is cheating to use anything that passed through C for
> > your cross-compiler, and it really would be *cheating*.
> OK, so you're going out of your way to avoid using C, for reasons
> I don't fully understand (or perhaps I do :-)

Well for starters, there's thing about how much of the camel's nose
do you allow into the tent. If you allow any use of C whatsoever,
there's a tendancy to get lazy and rely on C for just about
everything. It's sorta hypocritical to claim Lisp is better than C,
but for Lisp itself to rely on C all over the place.

> Although I would just have used assembler, if conventional HLLs
> were out; Lisp doesn't look that difficult to implement, at first
> glance...

One approach is to avoid all HLL (High Level Languages, I presume)
at all stages in bootstrapping, writing the barebones Lisp
interpretor in assembly language. But if the assembler itself was
written in C, we're getting into hypocritical territory again. It's
really difficult to be sure we aren't relying on C or other HLL
somehow unless we start from a bare machine such as an Altair with
front panel or PDP-10 with paper-tape of DDT.

> > Does anybody have a PDP-10 that still works?
> alt.sys.pdp10

Ah, thanks. I browsed the group last night, then posted a query
there just now before posting this.

> Apparently one just got sold on eBay.

How much did somebody pay for it? I saw an ad in the newsgroup
offering to sell one for a price that only a rich collector (for a
technology museum presumably) could afford.

Hmmm, I just thought of a neat hack: Get the old case of a PDP-10
that is no longer working (or fake one), strip out all the
electronics etc., put in a modern micro-computer with a PDP-10
emulator (including emulator for the kind of hard disks PDP-10s
used way back then so the emulated software can include an entire
operating system including device drivers), use the rest of the
space for storage or some other unrelated use to avoid wasting the
space, or use the extra space for a server farm. Pretend like the
hard disk (IBM 3330 or somesuch) is in the "basement", and have a
piece of floor that can be lifted up to "get to the basement" but
actually have a back-lit hologram so it *looks* as if there was a
huge basement full of disk drives.

Robert Maas, http://tinyurl.com/uh3t

unread,
Jul 6, 2008, 3:36:43 AM7/6/08
to
> Date: Thu, 26 Jun 2008 14:21:31 -0700 (PDT)> From: thomas.mer...@gmx.at

> > Are there standard packages available to provide these as
> > "givens" with a well-documented API so that different application
> > programmers can read each other's code?
> It is not the intention of Seed7 that everybody re-invents the
> wheel. There is a well-documented API. The predefined statements of
> Seed7 are described here:
> http://seed7.sourceforge.net/manual/stats.htm

Given that such statements aren't in the *core* of the language,
but are added later when the library containing their definitions
is loaded (possibly when building the executable that is saved on
the disk to avoid the cost of loading the library again each time
the executable is run):
- Does Seed7 include a parser that reads Seed7 source-code syntax
(from an input stream such as from a text file, or from the
contents of a string) and produces a parse tree (as a pointy
structure)?
- If so, does this parser automatically get enhanced whenever a new
statement type is defined in some library that was loaded, so
that statements defined in the library can now be parsed?
- If so, is there also an inverse function that prettyprints from a
parse tree back out to textual source-code syntax?
- If so, does that prettyprinter function also get automatically
enhanced whenever a new statement type is defined, so that
statements of that new type can be printed out meaningfully?
I ask because in Lisp it's almost trivial to write a utility that
reads in source code, analyzes it for some properties such as
undeclared free variables or functions etc., in order to build a
master cross-reference listing for an entire project and to flag
globally undefined functions, and also it's trivial to write code
that writes code and then either executes it with the same
core-image or prettyprints it to a file to be compiled and/or
loaded later. So I wonder if Seed7 provides the primitives needed
to make the same kinds of tasks equally easy for Seed7 sourcecode.

> Other types and their functions (methods) are described here:
> http://seed7.sourceforge.net/manual/types.htm

| boolean conv A Conversion to boolean
| ( Type of argument A: integer,
| boolean conv 0 => FALSE,
| boolean conv 1 => TRUE )
Is the behaviour defined for other values given? Does it throw an
exception, or are compiler writers free to treat other integers any
way they feel, resulting in code that produces different results
under different implementations? The Common Lisp spec is careful to
have undefined behaviour only in cases where the cost of
prescribing the behaviour would have a good chance of greatly
increasing the cost of implementation. Is such the case here?

> > Again, these are such basic container types that they really ought
> > to be provided in a standard package. Are they?
> A short explanation of Seed7 container types is here:
> http://seed7.sourceforge.net/faq.htm#container_classes

| ord(A) Ordinal number
| ( Type of result: integer,
| ord(FALSE) => 0, ord(TRUE) => 1 )

So this is just the inverse of boolean conv?

| succ(A) Successor
| ( succ(FALSE) => TRUE,
| succ(TRUE) => EXCEPTION RANGE_ERROR )
| pred(A) Predecessor
| ( pred(FALSE) => EXCEPTION RANGE_ERROR )
| pred(TRUE) => FALSE )

Why even bother, unless this is a hackish way of conditionally
signalling an exception?

| rand(A, B) Random value in the range [A, B]
| ( rand(A, B) returns a random value such that
| A <= rand(A, B) and rand(A, B) <= B holds.
| rand(A, A) => A,
| rand(TRUE, FALSE) => EXCEPTION RANGE_ERROR )

What distribution within that range, uniform or what?
Or implementation dependent? If some implementation wants to just
always return A with probability 1, that's conforming to the spec?
Or some other implementation wants to return A 90% of the time and
B 10% of the time and never any of the possible values strictly
between A and B, that would be conforming to the spec?
What sorts of datatypes are allowed for A and B?
Do they have to be of the same datatype, or can they be unrelated?
rand(3,9.5) rand(4.7SinglePrecision, 9.7DoublePrecision)
rand("FALSE",4.3) rand(FALSE,"TRUE") rand(TRUE,3)
Which if any of those expressions are conforming to the spec?

> Note that the 'and' and 'or' operators do not work correct when
> side effects appear in the right operand.

What is that supposed to mean??? If you use an OR expression to
execute the right side only if the left side is false, what
happens? I would expect what I said to happen, but the spec says
that doesn't work correctly? So what really happens?
(or (integerp x) (error "X isn't an integer")) ;Lisp equivalent

> The result an 'integer' operation is undefined when it overflows.

That's horrible! Does Seed7 provide any way to perform
unlimited-size integer arithmetic, such as would be useful to
perform cryptographic algorithms based on products of large prime
numbers, where the larger the primes are the more secure the
cryptographic system is?

| ! Faktorial

Is that how the word is spelled somewhere in the English-speaking world?

| div Integer division truncated towards zero
| ( A div B => trunc(flt(A) / flt(B)),
| A div 0 => EXCEPTION NUMERIC_ERROR )
| rem Reminder of integer division div
| ( A rem B => A - (A div B) * B,
| A rem 0 => EXCEPTION NUMERIC_ERROR )

Most CPUs, or software long-division procedures, compute quotient
and remainder simultaneously.
Is there any way for a Seed7 program to get them together?
It's wasteful to throw away the remainder then need to multiply the
quotient by the divisor and subtract to generate a copy of the
remainder that was thrown away a moment earlier.
(multiple-value-setq (q r) (floor a b)) ;Can do it in Lisp

| ** Power
| ( A ** B is okay for B >= 0,
| A ** 0 => 1,
| 1 ** B => 1,
| A ** -1 => EXCEPTION NUMERIC_ERROR )

So -1 ** -1 is required by the spec to signal an exception, instead
of giving the mathematically correct result of -1?
While 0 ** 0 which is mathematically undefined is *required* to return 1?

I'm too tired to proofread your spec any further.

> http://seed7.sourceforge.net/manual/types.htm#hash

| flip(A) Deliver a hash with keys and values flipped
| ( Type of result: hash [baseType] array keyType )

How is that possible if the table isn't a 1-1 mapping??

> > What precisely do you mean by "templates"?
> What computer scientists mean when they speak about "templates"
> is explained here:
> http://en.wikipedia.org/wiki/Template_%28programming%29

I.e. exactly what C++ implements, everything else is different and
not the same thing and substancard compared to C++ templates,
right?

> > ] Although functions can return arbitrary complex values (e.g. arrays of
> > ] structures with string elements) the memory allocated for all
> > ] intermediate results is freed automatically without the help of a
> > ] garbage collector.
> > How?????
> I have improved the FAQ for this. See:
> http://seed7.sourceforge.net/faq.htm#garbage_collection

| Memory used by local variables and parameters is automatically freed
| when leaving a function.

That doesn't makes sense to me. Suppose there's a function that has
a local variable pointing to an empty collection-object (set, list,
array, hashtable, etc.) allocated elsewhere. Now the function runs
a loop that adds some additional data to that collection-object, so
the object is now larger than before. Now the function returns. How
much of that collection-object is "memory used by local variables
and parameters" hence "automatically freed when leaving a
function", and how much of that collection-object is *not* such and
hence *not* automatically freed upon leaving the function?

> > Debug-use case: A application is started. A sematic error (file
> > missing for example) throws user into break package. User fixes the
> > problem, but saves a pointer to some structure in a global for
> > later study.
> You are much too deep in the Lisp way of thinking. If a stack
> shrinks the elements popped from the top just don't exist any more.

So it's impossible in Seed7 to have a function create an object and
return it, because anything created by a function doesn't exist any
more after return?

> It is also a common bug in C/C++ and other similar languages when
> a function returns a pointer to some local data (which is at the

> stack) ...

Are you saying that in Seed7 it's impossible to have any data that
is not on the stack, hence it's impossible for any function to
allocate memory for some object and then *return* a pointer to that
object so that the caller can later work with that object?

> If you want such data to be available later you need to make a
> (deep) copy.

That's an utterly horrible design requirement!!! That makes it
impossible to keep around multiple generations of a balanced search
tree which share all structure except log(n) path difference from
each version to the next, and hence it's impossible to insert or
delete etc. in log(n) time because of the need to make a complete
deep copy of the structure before you do the log(n) ins/del
operation on one of the two copies. Consider for example an
application that explores a space in a nested (tree-like) way, with
backtracking when it looks like some branch is not going to be
productive, but with continuations to re-activate a backed-from
branch when it turns out that the alternative is even less likely
to be productive. Each time it descends into a branch, it updates a
state tree in a non-destructive way so that both the current
branch-state (and any continuation of it) can co-exist with the
parent-state. As an example of such an algorithm, each branch is
evaluated as to likelihood of success, and a priority queue,
probably implemented as a HEAP (as in heapsort) is used to keep
track of all the currently pending branches in the search tree,
with each entry in the priority queue being one of the
balanced-binary-search state-trees. So with shared structures, as
is easy in Lisp, it takes log(n) time to pick the next branch to
explore, where n is the number of active branches, and it takes
log(k) time to non-destructively modify a branch-state BST when
moving from one branch to a sub-branch of it. It doesn't sound like
such an algorithm is even possible in Seed7, at least not with
log(k) time, more like k time to make a complete copy of the BST
each time it's modified while needing to keep the original too.

> > ;Static type checking fails to detect this type-mismatch undefined-method error.

> ... Every bug found at compile-time will not make you trouble at


> run-time. The earlier you can eliminate bugs the better.

Why do you feel the need to have two different times, one where
static stuff is checked but you have no idea what's really
happening, and then one where things actually happen? I suppose in
Seed7 it's impossible to have an interactive loop where you can
type in a line of code and it *immediately* does something and you
*immediately* see whether it did what you expected it to do instead
of needing to wait until the whole program is compiled before you
can see what that one line of code did?

> ... There is also the type 'bigInteger' which serves as


> unlimited-size signed integer. The type 'bigInteger' is explained
> here:
> http://seed7.sourceforge.net/manual/types.htm#bigInteger

| div Integer division truncated towards zero
| ( A div B => trunc(A / B),
| A div 0_ => EXCEPTION NUMERIC_ERROR )
| rem Reminder of integer division div
| ( A rem B => A - (A div B) * B,
| A rem 0_ => EXCEPTION NUMERIC_ERROR )

For bigIntegers, it's especially painful not to have division with
both quotient and remainder directly returned. It takes a lot of
extra time to multiply the quotient by the divisor (then subtract
that from the original value) to get back the remainder after
having thrown away the remainder in the first place.

> > What is the precise meaning of type 'char'?
> The 'char' values use the UTF-32 encoding, see:
> http://seed7.sourceforge.net/manual/types.htm#char

| The type 'char' describes UNICODE characters. The 'char' values use
| the UTF-32 encoding. In the source file a character literal is written
| as UTF-8 UNICODE character enclosed in single quotes. For example:
| 'a' ' ' '\n' '!' '\\' '2' '"' '\"' '\''

That's not written well at all. It doesn't make sense. \n is not a
UTF-8 coding of a single character, it's two different UTF-8
(US-ASCII subset thereof) characters which are a C convention for
newline.
Likewise \\ is two UTF-8 (US-ASCII) characters.
Likewise \" is two UTF-8 (US-ASCII) characters.
Likewise \' is two UTF-8 (US-ASCII) characters.

I don't think you have any idea what UTF-8 really means.

> AFAIK Java and C# use immutable strings. If you concatenate a lot
> of elements to a string the immutable strings are copied a lot.

You don't know what you're talking about. There is no method in
Java that given a String object adds an element to it. There is an
*operator* (effectively a function but using infix syntax) that
concatenates two or more strings to produce a new string, i.e. what
you have is an ordinary expression that takes input values and
returns a new value, not an instance method that modifies an object
or even tries to modify an object.

> This is the reason that Java introduced the mutable strings
> classes StringBuffer and StringBuilder.

I don't see StringBuilder in the JavaDoc for the API online, so
maybe that's something brand-new in the most recent version of
Java. Anyway, yes StringBuffer *does* have methods for adding new
characters to the end of the string it represents.

But all this is irrelevant. Java's class String is a special case,
one of the very few immutable-object classes in Java. Saying "if
you concatenate to a String" is wrong, because you simply cannot
contactenate to a String in the first place. In general, objects
(with these rare exceptions) are mutable in Java and Lisp etc.
If a language has no mutable objects whatsoever, if the slightest
change to *any* object requires performing a deep copy to build a
whole new object, that IMO is not a good language design.

Jon Harrop

unread,
Jul 9, 2008, 5:35:09 PM7/9/08
to
Robert Maas, http://tinyurl.com/uh3t wrote:
>> Date: Sun, 22 Jun 2008 13:13:13 +0100
> Why this response is so belated:
> <http://groups.google.com/group/misc.misc/msg/cea714440e591dd2>
> = <news:rem-2008...@yahoo.com>
>> From: Jon Harrop <j...@ffconsultancy.com>
>> Haskell, SML, OCaml, Mathematica, F# and Scala all allow real
>> problems to be solved much more concisely than with Lisp. Indeed, I
>> think it is difficult to imagine even a single example where Lisp
>> is competitively concise.
>
> What does "solved much more concisely" mean??

The solutions are shorter in modern FPLs.

> So why don't you say what you really mean, that some particular
> application types are better represented in published libraries in
> your favorite language than in mine (Lisp)?

That is neither what I meant nor what I said.

> And people who like
> Lisp will respond that some *other* application types are better
> represented in published libraries in Lisp language than in
> *yours*? Why don't you compile a list of all the major application
> types that you feel are needed by the largest number of potential
> customers/users, and allow the rest of us to propose additions to
> your list? For example, IMO the most commonly useful application
> types are:
> - Business operations (accounts receivable/payable, inventory, payroll)
> - Text editing (both plain text and "word processing" formatted text)
> - Web browsing/surfing/searching
> - E-mail (both plain text and MIME multi-media) including protection
> against spam - Conferencing and other public/group forums
> - Security, including protection against computer
> viruses/worms/trojans/bots There are probably five or ten more that I
> could add if I had the time/energy to think of them. Anyway, after we get
> a complete list, based on concensus of everyone reading comp.programming
> on a regular basis and seeing this thread, *then* you need to say how
> well your favorite language addresses these common application
> needs.
>
> Note that algebraic datatypes are not among the major applications I
> listed.

Common != useful.

> ...


> (defcurry quadratic (a) (b c x) (+ (* a x x) (* b x) c))

> ...


>> In F#:
>> let inline quadratic a b c x = a*x*x + b*x + c
>
> That's more verbose than my use of defcurrypoly.

No it isn't. Also, you did not define "defcurry".

> Does F# provide a way to carry out the abstraction of defcurrypoly?

The "inline" did precisely that.

> Also, it's absurd that the keyword for generating a curried
> function is "inline" instead of "curry".

That is not what the "inline" was for.

Here is the OCaml/F# including the definition of the searching function:

let nal1 =
[ "USA", ("1", [ "Oregon", ["503"; "541"; "971"];
"Nevada", ["702"; "775"];
"NorthDekota", ["701"] ]);
"Canada", ("1", ["Manatoba", ["204"]]);
"Belgium", ("32", ["Antwerp", ["3"]; "Brussels", ["2"]]);
"Croatia", ("385", ["Dubrovnik", ["20"]; "Zagrev", ["1"]]) ]

let search key map k =
let h, t = List.assoc key map in
h :: k t

search "Croatia" nal1 (List.assoc "Dubrovnik")

The Mathematica is essentially the same as the Lisp because it also lacks
static type checking.

>> ... modern functional languages like Haskell, OCaml and F# ...
>> They [users] clearly do not have a problem with the supreme brevity
>> of these languages.
>
> In cases where those particular languages are not already brief as
> delivered, how easy is it to define a shorter syntax if you're
> going to want a particular operation very terse because it's going
> to be done a lot? I.e. how do they compare with Lisp's ability to
> define macros?

Macros are very rare in modern languages. OCaml has a full macro system but
it is rarely used. F# has no macro system.

>> ... I was listing some of Lisp's most practically-important
>> deficiencies. Lack of static typing in the language is one.
>
> I disagree. I pointed out how static typing doesn't really solve
> the problem at all because it's incapable of dealing with
> intentional data types which is where the *real* problem lies.
> Static type-checking of internal datatypes doesn't hardly begin to
> solve the real problem. It's just an advertising gimmick that turns
> out to be more cost than value.

Why do you think statically typed languages completely dominate general
purpose programming?

> (The exception is in tight loops
> where some machine type happens to work efficiently and where that
> particular loop eats up most of the CPU time, which is precisely
> where Lisp allows type declarations and consequent CPU-efficient
> code as an *option* where it would actually be worth the cost.)

Lisp is unable to regain the performance cost of its dynamic design:

http://www.ffconsultancy.com/languages/ray_tracer/results.html

>> Lack of concurrent GC in all Lisp implementations is another.
>
> I gotta look up that to see what the term means ... first match is:
> <http://xiao-feng.blogspot.com/2007/03/about-mostly-concurrent-gc.html>
> That gives me some idea, but it doesn't seem like what you desire, right?

That is a step in the right direction.

The problem is that my tests were all ideally suited to distributed
parallelism when, in fact, even embarassingly parallel programs often
require substantial scattering and gathering of data. Having a
concurrent GC makes it possible for programs to share mutable data
structures which removes the cost of scatter and gather.

For example, I found that a parallel matrix multiply is up to 100x faster in
F# than OCaml:

http://groups.google.com/group/fa.caml/msg/c3dbf6c5cdb3a898

For programs like this (which are very common), scattering and gathering are
free with a concurrent GC but can be extremely expensive without.
Consequently, F# sees performance improvements from parallelization for a
much wider variety of tasks than OCaml can.

> So anyway, whether concurrent GC is worth the trouble or not, is
> debatable.

If you have >1 core, it is not debateable.

>> weak references,
> [suffix "are all fundamental deficiencies of the language" for
> these next several items]
>
> It seems to me that a GC hook could be used to purge transient data
> that hasn't been referenced recently. How easy is it to actually
> implement this and get it working well with only ANSI CL to work
> with?

AFAIK, it is inherently implementation dependent.

>> finalizers,
>
> You mean like to automatically close a system resource whenever the
> Lisp hook for it is GC'd? Normally system resources like that are
> lexically bound, whereby UNWIND-PROTECT is sufficient to make sure
> they get closed when that block of code is exited. Is there
> *really* a good reason to have such resources kept around per data
> object instead of per block-entry/exit?

Yes. Finalizers are useful for the same reason that GC itself is good (and
Ada is bad): you don't want to have to shoehorn everything you do into
scopes just to ensure that things get freed correctly. Another reason is
that garbage collectors often free sooner than that.

> Somehow it seems to me a
> dangerous design to have system resources that get closed at
> "random" times (when memory runs short) instead of at clearly
> defined times.

Absolutely. Finalizers are not suitable if you require determinism.

> But if you can think of a really good use for random closure, I may
> reconsider my opinion.

I used finalizers in OCaml to collect unused OpenGL display lists and
textures. The results were excellent: the code was greatly simplified at no
cost.

>> asychronous computations,
>
> I'm not sure what you mean. Don't compilers already generate
> efficient CPU data+instruction parallel-pipelining whenever they
> detect that two operations are done in close proximity neither of
> which depends on the result of the other?

I was referring to the use of green threads from a monadic style, like F#'s
asynchronous workflows. You need to rewrite the code to CPS which can be
done in Lisp but is not provided as standard (and would be useful for a
wide variety of concurrent programming tasks).

>> memory overflow recovery,
>
> That might be moot on typical modern architectures that have a
> small CPU cache and a large RAM and a truly gigantic virtual
> address space. Locality of reference to avoid thrashing is a
> problem long before total virtual memory or disk space for swapping
> is ever exceeded.

The vast majority of computers do not have virtual memory because they are
embedded devices.

>> In a serial GC, the program often (e.g. at every backward branch)
>> calls into the GC to have some collection done. Collections are
>> typically done in small pieces (incrementally) to facilitate
>> soft-real time applications but can only use a single core. For
>> example, OCaml has a serial GC so OCaml programs wishing to use
>> multiple cores fork multiple processes and communicate between them
>> using message passing which is two orders of magnitude slower than
>> necessary, largely because it incurs huge amounts of copying that
>> is not necessary on a shared memory machine:
>
> On a shared-memory machine, how difficult is it to prevent
> different processes from trying to read and write the same object
> at the same time, whereupon the writer has only partly overwritten
> the object when the reader starts to read it and sees pieces of old
> and new object scrambled together?

Depends who you ask. The Java community don't seem to have a problem but the
Haskell community will tell you that it is impossible (they often tell me
that what I do is impossible).

>> With a concurrent GC, the garbage collector's threads run
>> concurrently with the program threads without globally suspending
>> all program threads during collection. This is scalable and can be
>> efficient but it is incredibly difficult to implement correctly.
>> The OCaml team spent a decade trying to implement a concurrent GC
>> and never managed to get it working, let alone efficient.
>
> Maybe it's just too tricky (to implement correctly) to be worth
> doing?

If that were true then the JVM and .NET would not be worth having but, in
reality, they are two of the most highly valued pieces of software ever
written.

> Maybe the Lisp implementors were wise not to make that gamble?

The Lisp implementors had no choice: they cannot write a concurrent GC
because it is too hard for them. Same goes for all other open source FPLs as
well.

> Maybe it's "not ready for prime time", i.e. until somebody
> finds a way to get it right, it's not worth fretting over the fact
> that it hasn't been done, it's better to just live without it?

The JVM and .NET are prime time.

>> > - List several kind of applications and/or API tools that are
>> > hampered by lack of whatever that means.
>>
>> Any software that requires fine-grained parallelism for
>> performance will be hampered by the lack of a concurrent GC.
>
> List five such applications, and convince me that any technique
> other than fine-grained parallelism is too inefficient for each
> such application.

Polygon tesselation, Fast Fourier transform, LU decomposition, suffix trees,
the distinct element method...

> Also convince me in each case that circular
> structure is absolutely necessary, because after all if there are
> no pointer loops then reference counting works just fine and no GC
> is needed at all. Convice me that a hybrid system, using a symbol
> table of tops of structures, with no loops within any of the
> structures except that symbols in the symbol table may be
> mentionned within structures, would not be sufficient for each such
> application.

In theory, you can code everything in assembler. In practice, it is not
feasible. The same applies here: high-level languages make all of those
parallel tasks much easier.

>> >> 2. Even though Lisp's forte is as a language laboratory, Lisp has
>> >> so little to offer but costs so much
>> > Um, some implementations of Common Lisp are **free** to download
>> > and then "use to your heart's content". How does that cost too much???
>> Development costs are astronomical in Lisp compared to modern
>> alternatives like F#, largely because it lacks a static type system
>
> As I pointed out before, static type-checking doesn't solve enough
> of the "problem" to be worth the trouble it causes.

I disagree.

> Any language that imposes static typing is what slows down development.

I have found that modern statically typed languages let me develop
complicated applications much faster than any dynamic language.

>> decent developer tools like IDEs
>
> I used to use Macintosh Allegro Common Lisp on my Mac Plus.
> Wasn't that a IDE?

Compare it to Mathematica (which has the world's best IDE, IMHO).

>> There are no decent Lisp implementations for .NET.
>
> Why does anybody care? What, if anything, is good about .NET?

Lots of wealthy .NET users leading to high profits is good.

>> Finally, there is no way you'll ever turn a profit because the
>> market for commercial third-party software for Lisp is too small.
>
> Consider leased server-side applications (CGI or PHP). How would
> the customer know or care whether Lisp or some other language
> happens to be used to implement it?

In theory, they would not care. In practice, I am not familiar with any such
businesses.

> Just about everyone on the net
> uses the Google search engine. It's advertising based rather than
> subscription, but ignore that detail for a moment. Do you know what
> language(s) are used to implement all its internals?

Mostly C/C++.

> Do you care?

Yes but as a programmer rather than a Googler.

>> I think you should aspire to earn money directly from customers
>> rather than asking people to give you money to develop your
>> software.
>
> I like that idea very much. Will you find me a potential customer
> who wants some serverside application that doesn't already exist?

You need to write a compelling sales page and make it indexable by search
engines and make it as easy as possible for users to give you their money
before plastering all of the news websites that you can find with
descriptions of your new service and links to it. Then you need to
repeatedly update it and report new news stories about it for many months
before you will start to see significant website hits. Only hits on sales
pages correlate with sales.

As a guide, our products make around 20p profit per page view.

Alf P. Steinbach

unread,
Jul 9, 2008, 5:44:13 PM7/9/08
to
* Jon Harrop:

> Robert Maas, http://tinyurl.com/uh3t wrote:
>> So anyway, whether concurrent GC is worth the trouble or not, is
>> debatable.
>
> If you have >1 core, it is not debateable.

This seems to be a misconception. Having GC run on its own core is only "free"
(not quoting you) for a single-threaded program running in a single-tasking
operating system. It seems to be the same kind of misconception that generated
the SPARC register "window" system to avoid going to main memory for stack --
where end result is just more overhead for thread and process context switches.

Not that GC on own core isn't nice.

It's just not free, and not not debatable. ;-)


Cheers,

- Alf

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?

thomas...@gmx.at

unread,
Jul 11, 2008, 6:49:28 AM7/11/08
to
On 6 Jul., 09:36, jaycx2.3.calrob...@spamgourmet.com.remove (Robert

Maas, http://tinyurl.com/uh3t) wrote:
> > Date: Thu, 26 Jun 2008 14:21:31 -0700 (PDT)
>
> Why this response is so belated:
> <http://groups.google.com/group/misc.misc/msg/cea714440e591dd2>
> = <news:rem-2008...@yahoo.com>
Anyway, thank you for the feedback.

> > From: thomas.mer...@gmx.at
> > > Are there standard packages available to provide these as
> > > "givens" with a well-documented API so that different application
> > > programmers can read each other's code?
> > It is not the intention of Seed7 that everybody re-invents the
> > wheel. There is a well-documented API. The predefined statements of
> > Seed7 are described here:
> >http://seed7.sourceforge.net/manual/stats.htm
>
> Given that such statements aren't in the *core* of the language,
> but are added later when the library containing their definitions

> is loaded ...
Actually the statements are added during the parsing process.

> (possibly when building the executable that is saved on
> the disk to avoid the cost of loading the library again each time
> the executable is run):
> - Does Seed7 include a parser that reads Seed7 source-code syntax
> (from an input stream such as from a text file, or from the
> contents of a string) and produces a parse tree (as a pointy
> structure)?

Actually there are the functions 'parseFile' and 'parseStri' which
can be used to parse Seed7 source programs into a values of the
type 'program'. I just added a short description about the type
'program' to the manual. See:
http://seed7.sourceforge.net/manual/types.htm#program

Note that I try to handle the program currently executed and the
program which was parsed into a 'program' variable to be separate.
It is possible to execute 'program' values and to request the code
of a 'program' value in a structured form. Actually the Seed7 to C
compiler uses this feature to generate the C code. Currently the
features of this reflection are designed to make them usable for the
compiler.

It is not my intend to support programs which manipulaten their own
code as it is done with "self modifying code".

> - If so, does this parser automatically get enhanced whenever a new
> statement type is defined in some library that was loaded, so
> that statements defined in the library can now be parsed?

Yes. This is something happening during the parsing process.
Loading a library at runtime as a way to introduce new statements
for the program which is currently running IMHO makes no sense.

> - If so, is there also an inverse function that prettyprints from a
> parse tree back out to textual source-code syntax?

During the parsing some information, such as whitespace and comments
are lost. Some information about the position of an expression
is maintained. Generally I think that such a prettyprinter would be
possible.

> - If so, does that prettyprinter function also get automatically
> enhanced whenever a new statement type is defined, so that
> statements of that new type can be printed out meaningfully?

I have nothing done in this direction, but I think that that should
be possible. It might me necessary to extend the the reflection, if
some functionality necessary for prettyprinting, is missing.

> I ask because in Lisp it's almost trivial to write a utility that
> reads in source code, analyzes it for some properties such as
> undeclared free variables or functions etc., in order to build a
> master cross-reference listing for an entire project and to flag
> globally undefined functions, and also it's trivial to write code
> that writes code and then either executes it with the same
> core-image or prettyprints it to a file to be compiled and/or
> loaded later. So I wonder if Seed7 provides the primitives needed
> to make the same kinds of tasks equally easy for Seed7 sourcecode.

Such things are planned for Seed7. I have started with a program
which generates html documentation including a source file where
every use of a function is linked to its definition. The program
(doc7) works to some degree, but not good enough to release it.

> > Other types and their functions (methods) are described here:
> >http://seed7.sourceforge.net/manual/types.htm
>
> | boolean conv A Conversion to boolean
> | ( Type of argument A: integer,
> | boolean conv 0 => FALSE,
> | boolean conv 1 => TRUE )
> Is the behaviour defined for other values given? Does it throw an
> exception, or are compiler writers free to treat other integers any
> way they feel, resulting in code that produces different results
> under different implementations? The Common Lisp spec is careful to
> have undefined behaviour only in cases where the cost of
> prescribing the behaviour would have a good chance of greatly
> increasing the cost of implementation. Is such the case here?

This function was added short ago to be helpful for the I4 P-Code
interpreter which would be used for the P4 Pascal compiler.
Yes, I transfered this classic Pascal compiler to Seed7...
The I4 interpreter needs cheap functions to transfer all its
basic types like boolean, float, char, set to and from integer.
The Pascal version of the I4 interpreter uses an unchecked
cased record (which is equivalent to a C union). Therefore I
introduced this function. It was a mistake to add this function
to the documentation, since it is currently only experimental.
BTW it works like the odd function.

> > > Again, these are such basic container types that they really ought
> > > to be provided in a standard package. Are they?
> > A short explanation of Seed7 container types is here:
> >http://seed7.sourceforge.net/faq.htm#container_classes
>
> | ord(A) Ordinal number
> | ( Type of result: integer,
> | ord(FALSE) => 0, ord(TRUE) => 1 )
>
> So this is just the inverse of boolean conv?

Yes, but the introduction of 'boolean conv' was just experimental.
The function 'odd(integer)' is the function to be preferred
to convert an integer into a boolean.

> | succ(A) Successor
> | ( succ(FALSE) => TRUE,
> | succ(TRUE) => EXCEPTION RANGE_ERROR )
> | pred(A) Predecessor
> | ( pred(FALSE) => EXCEPTION RANGE_ERROR )
> | pred(TRUE) => FALSE )
>
> Why even bother, unless this is a hackish way of conditionally
> signalling an exception?

Such functions are present to be usable in generic code. That way
the generic code can assume that the 'succ' function is present. The
'incr(A)' function, which is just a shortcut for 'A := succ(A)', is
also present just for this purpose. An example of a template
function which uses 'incr' is here:
http://seed7.sourceforge.net/examples/for_decl.htm

> | rand(A, B) Random value in the range [A, B]
> | ( rand(A, B) returns a random value such that
> | A <= rand(A, B) and rand(A, B) <= B holds.
> | rand(A, A) => A,
> | rand(TRUE, FALSE) => EXCEPTION RANGE_ERROR )
>
> What distribution within that range, uniform or what?

Uniform. I added an explanation to the documentation.

> What sorts of datatypes are allowed for A and B?
> Do they have to be of the same datatype, or can they be unrelated?
> rand(3,9.5) rand(4.7SinglePrecision, 9.7DoublePrecision)
> rand("FALSE",4.3) rand(FALSE,"TRUE") rand(TRUE,3)
> Which if any of those expressions are conforming to the spec?

There is a general rule to keep the descriptions short. This rule
can be found at the beginning of the chapter "PREDEFINED TYPES":
The operators have, when not stated otherwise, the type described
in the subchapter as parameter type and result type.

> > Note that the 'and' and 'or' operators do not work correct when
> > side effects appear in the right operand.
>
> What is that supposed to mean??? If you use an OR expression to
> execute the right side only if the left side is false, what
> happens? I would expect what I said to happen, but the spec says
> that doesn't work correctly? So what really happens?

This is a bug in the spec. I have corrected the sentence to:
Note that this early termination behaviour of the 'and' and 'or'
operators also has an influence when the right operand has side
effects.

> (or (integerp x) (error "X isn't an integer")) ;Lisp equivalent
>
> > The result an 'integer' operation is undefined when it overflows.
>
> That's horrible!

AFAIK many languages such as C, C++ and Java have this behaviour.
I would like to raise exceptions in such a case, but as long
as there is no portable support for that in C, Posix or some
other common standard, it would be hard to support it with
satisfactory performance.

> Does Seed7 provide any way to perform
> unlimited-size integer arithmetic, such as would be useful to
> perform cryptographic algorithms based on products of large prime
> numbers, where the larger the primes are the more secure the
> cryptographic system is?

Yes.

> | ! Faktorial
>
> Is that how the word is spelled somewhere in the English-speaking world?

This is how a word looks like when it is not translated correctly.
Thank you for pointing this out.

> | div Integer division truncated towards zero
> | ( A div B => trunc(flt(A) / flt(B)),
> | A div 0 => EXCEPTION NUMERIC_ERROR )
> | rem Reminder of integer division div
> | ( A rem B => A - (A div B) * B,
> | A rem 0 => EXCEPTION NUMERIC_ERROR )
>
> Most CPUs, or software long-division procedures, compute quotient
> and remainder simultaneously.
> Is there any way for a Seed7 program to get them together?

Currently not, but it is not hard to add such a thing.

> It's wasteful to throw away the remainder then need to multiply the
> quotient by the divisor and subtract to generate a copy of the
> remainder that was thrown away a moment earlier.

I guess that a good optimizing compiler can recognize the situation
when 'a div b' and 'a rem b' are computed close together without
changing a or b in between. Since Seed7 is compiled to C, I think
that I can rely on the C compiler to do this optimisation.

> (multiple-value-setq (q r) (floor a b)) ;Can do it in Lisp
>
> | ** Power
> | ( A ** B is okay for B >= 0,
> | A ** 0 => 1,
> | 1 ** B => 1,
> | A ** -1 => EXCEPTION NUMERIC_ERROR )
>
> So -1 ** -1 is required by the spec to signal an exception, instead
> of giving the mathematically correct result of -1?

In the general case 'a ** -1' does not have an integer result.
AFAIK Ada also does it that way.
BTW: The type float also has exponentiation operators defined.

> While 0 ** 0 which is mathematically undefined is *required* to return 1?

This behaviour is borrowed from FORTRAN, Ada and some other
programming languages which support exponentiation.

> I'm too tired to proofread your spec any further.
>
> >http://seed7.sourceforge.net/manual/types.htm#hash
>
> | flip(A) Deliver a hash with keys and values flipped
> | ( Type of result: hash [baseType] array keyType )
>
> How is that possible if the table isn't a 1-1 mapping??

That's the reason the result is of type:
hash [baseType] array keyType.
The values in the hash tables are arrays with keyType elements.

> > > What precisely do you mean by "templates"?
> > What computer scientists mean when they speak about "templates"
> > is explained here:
> >http://en.wikipedia.org/wiki/Template_%28programming%29
>
> I.e. exactly what C++ implements, everything else is different and
> not the same thing and substancard compared to C++ templates,
> right?

Wrong. I use the word template to describe a function which is
executed at compile time and declares some things while executing
(at compile time). For example: The function 'FOR_DECLS' is used to
declare for loops. FOR_DECLS gets a type as parameter and declares
a for loop for that type. This is explained here:
http://seed7.sourceforge.net/examples/for_decl.htm

As you can see it is necessary to call template functions explicit.
They are not invoked implicit as the C++ template functions.
IMHO this explicit calls of template functions make the program
easier to read. Maybe I should add something to the FAQ.

> > > ] Although functions can return arbitrary complex values (e.g. arrays of
> > > ] structures with string elements) the memory allocated for all
> > > ] intermediate results is freed automatically without the help of a
> > > ] garbage collector.
> > > How?????
> > I have improved the FAQ for this. See:
> >http://seed7.sourceforge.net/faq.htm#garbage_collection
>
> | Memory used by local variables and parameters is automatically freed
> | when leaving a function.
>
> That doesn't makes sense to me. Suppose there's a function that has

> a local variable pointing ...
Pointers are something else. If you are using pointers you are
responsible to manage that they point to reasonable data.

The automatic freeing of local variables has exceptions (sorry
I will add an explanation to the FAQ). The values referred by
pointers and the values refered by interface types are not managed
automatically.

> to an empty collection-object (set, list,
> array, hashtable, etc.) allocated elsewhere. Now the function runs
> a loop that adds some additional data to that collection-object, so
> the object is now larger than before. Now the function returns. How
> much of that collection-object is "memory used by local variables
> and parameters" hence "automatically freed when leaving a
> function", and how much of that collection-object is *not* such and
> hence *not* automatically freed upon leaving the function?

I see. We have a cultural misunderstanding.
Lets say the collection is an array.
What you were suggesting is a collection declared with:

array ptr myData

which means that the collection contains pointers to myData
(some structure). In this case you are right and automatic
management is not possible (at least in the sense I talked about).

What I have in my mind when talking about automatic managed memory
is a collection declared with:

array myData

In this case the collection contains (copies of) the actual data.
When such a collection is freed it can also free its content
since it owns it. And this are the things which done automatically
in a stack oriented manner.

If you use pointer structures for everything you are right that
a GC or a manually managed heap is necessary. In Seed7 many things
can be done with abstract datatypes.

If abstract datatypes are used in an efficient way there is not
so much need to use pointers in Seed7 is not so high as in some
other languages.

> > > Debug-use case: A application is started. A sematic error (file
> > > missing for example) throws user into break package. User fixes the
> > > problem, but saves a pointer to some structure in a global for
> > > later study.
> > You are much too deep in the Lisp way of thinking. If a stack
> > shrinks the elements popped from the top just don't exist any more.
>
> So it's impossible in Seed7 to have a function create an object and
> return it, because anything created by a function doesn't exist any
> more after return?

The return variable is excluded from this mechanism.


>
> > It is also a common bug in C/C++ and other similar languages when
> > a function returns a pointer to some local data (which is at the
> > stack) ...
>
> Are you saying that in Seed7 it's impossible to have any data that
> is not on the stack, hence it's impossible for any function to
> allocate memory for some object and then *return* a pointer to that
> object so that the caller can later work with that object?
>
> > If you want such data to be available later you need to make a
> > (deep) copy.
>

> That's ...
[snip reasons why not all data should be stack oriented]
I agree that some data cannot be managed in a stack oriented way.

> > > ;Static type checking fails to detect this type-mismatch undefined-method error.
> > ... Every bug found at compile-time will not make you trouble at
> > run-time. The earlier you can eliminate bugs the better.
>
> Why do you feel the need to have two different times, one where
> static stuff is checked but you have no idea what's really
> happening, and then one where things actually happen?

I think that compile-time type checking can find bugs which
slip through the fingers when you test your program.
IMHO even a test with 100% code coverage is not sufficient since
the combination of all places where values are generated and
all places where this values are used must be taken into account.
I have aggain improved the FAQ to contain this argumentation:
http://seed7.sourceforge.net/faq.htm#static_type_checking

> I suppose in
> Seed7 it's impossible to have an interactive loop where you can
> type in a line of code and it *immediately* does something and you
> *immediately* see whether it did what you expected it to do instead
> of needing to wait until the whole program is compiled before you
> can see what that one line of code did?

No. There is the type 'program' which can be used for that.

BTW the Seed7 parser usually processes 200000 lines per second.

> > ... There is also the type 'bigInteger' which serves as
> > unlimited-size signed integer. The type 'bigInteger' is explained
> > here:
> >http://seed7.sourceforge.net/manual/types.htm#bigInteger
>
> | div Integer division truncated towards zero
> | ( A div B => trunc(A / B),
> | A div 0_ => EXCEPTION NUMERIC_ERROR )
> | rem Reminder of integer division div
> | ( A rem B => A - (A div B) * B,
> | A rem 0_ => EXCEPTION NUMERIC_ERROR )
>
> For bigIntegers, it's especially painful not to have division with
> both quotient and remainder directly returned. It takes a lot of
> extra time to multiply the quotient by the divisor (then subtract
> that from the original value) to get back the remainder after
> having thrown away the remainder in the first place.

When there is demand, such a function can be added.

> > > What is the precise meaning of type 'char'?
> > The 'char' values use the UTF-32 encoding, see:
> >http://seed7.sourceforge.net/manual/types.htm#char
>
> | The type 'char' describes UNICODE characters. The 'char' values use
> | the UTF-32 encoding. In the source file a character literal is written
> | as UTF-8 UNICODE character enclosed in single quotes. For example:
> | 'a' ' ' '\n' '!' '\\' '2' '"' '\"' '\''
>
> That's not written well at all. It doesn't make sense.

Agree. Historically this where just character literal examples.
Some of them use escape sequences, which were explained below.
Now you get the impression that this are UTF-8 literal
examples which was not the original intend.

> UTF-8 coding of a single character, it's two different UTF-8
> (US-ASCII subset thereof) characters which are a C convention for
> newline.
> Likewise \\ is two UTF-8 (US-ASCII) characters.
> Likewise \" is two UTF-8 (US-ASCII) characters.
> Likewise \' is two UTF-8 (US-ASCII) characters.

This are escape sequences. They are explained in the next paragraph.
I have moved the character literal examples after the explanation
of escape sequences. That way you don't get the impression that
this is an explanation of UTF-8 literals.

> I don't think you have any idea what UTF-8 really means.

As I have implemented UTF-8 support for Seed7, I think I know
something about it.

Robert Maas, http://tinyurl.com/uh3t

unread,
Jul 26, 2008, 4:43:34 AM7/26/08
to
> >> Haskell, SML, OCaml, Mathematica, F# and Scala all allow real
> >> problems to be solved much more concisely than with Lisp. Indeed, I
> >> think it is difficult to imagine even a single example where Lisp
> >> is competitively concise.
> > What does "solved much more concisely" mean??
> From: Jon Harrop <j...@ffconsultancy.com>

> The solutions are shorter in modern FPLs.

OK, let's say you have a text string which contains the notation
for a nested list starting from the beginning of the string. How
many lines of code, in various modern FPLs, would it take to parse
that nested-list notation to produce an actual nested list
structure, and also report back in another value the position in
the string where the parse left off at the end of the nested-list
notation? In Common Lisp it's just one line of code:
(multiple-value-setq (ls ei) (read-from-string ts))
where ts has the given text string,
ls gets the resultant list structure,
and ei gets the resultant end index.

Now let's say you want to pick up where you left off, because after
that first nested list notation there's another within the same
text string you were given. How many lines of code in your favorite
FPLs? In Common Lisp, it's another one line of code:
(multiple-value-setq (ls2 ei2) (read-from-string ts :start ei))

My point is that different languages are based on different things
being important enough to put into the core language, but with
add-on libraries it's possible to make a lot of things be
essentially the same length in a whole bunch of languages. If you
don't mind Unix-style ultra-short names for commonly use utilities,
and you do a lot of this kind of stuff, if you really want to make
your code as short as possibly, you can use macros to abbreviate
the special operator and function used above, so the two lines of
code will easily fit into just one run-on line of code:
(mvs (ls ei) (rfs ts)) (mvs (ls2 ei2) (rfs ts :start ei))

So I propose when comparing languages that we allow liberal use of
macros (in languages such as Lisp which support them) to tailor the
function names to the same length in all languages, so that when we
look at the code we are comparing the size of the parse tree and
*not* the relative lengths of names of functions. And furthermore
we allow add-on libraries to compress the size of the parse tree in
the application itself down to just the part that's different from
one application to another. So the only real difference is whether
the syntax of the language (with all available help from macros and
libraries) supports the most natural expression of the application
of the tool(s).

So let's do a check-list. Which languages allow natural expression of:
- case expresssions (not case statements!)
- loops with a typical wide variety of ways to map various indices/pointers
across lists/ranges
- anonymous functions which lexically close variables from the
surrounding lexical scope
- a mix of symbols which are in a master table so that they can be
canonicalized upon input and symbols that are *not* in such a
master table so that multiple instances by the same name can be
created at runtime without them interferring with each other, and
where each symbol of either type can have an unlimited number of
tagged properties hanging off it, including the traditional
variable (value) and function-definition
- formal specification of the parse tree of source code as data
that can be manipulated at runtime just the same as ordinary data
can
- choice or mix of two ways to specialize functions and datatypes,
either by having a single function defined in one place which
explicitly dispatches on the type(s) of its parameters, and/or
having multiple definitions of the same-name function distributed
into multiple places where keeping track of all the variants of the
same-name function is automatic and dispatching from the same
generic function per parameter datatypes is set up automatically
- virtually identical semantics between code typed directly into a
read-eval-print loop one line at a time and lines of code embedded
within function definitions which are either entered from the REP
as a unit or loaded from a source file en masse or compiled to an
object file and then that file loaded, with easy intermix of all
four modes of lines-of-codes
Common Lisp can do all of those. How many of your favorite FPLs can?

> Macros are very rare in modern languages.

Why? Because their parse tree isn't well defined and documented, so
it would be impossible for the author of a macro (in the Lisp
sense) to know what to do? Or because the syntax of the source code
is so different from the parse tree that it would be just too
difficult to mentally keep switching back and forth between
thinking about the parse tree (when designing the function of the
macro, what the macro is supposed to do) and writing code
(including the code of the macro-expander itself)? Or because
authors of most modern languages are unaware how valuable macros
can be in improving the "out of the box" syntax to be better fit a
problem domain or to capture the essense of common design patterns
into a single syntax?

Let's think about design patterns more. The whole idea of a design
pattern is that the language itself doesn't support direct
expression of a design, so the programmer must constantly think of
the pattern and write code in that form to put the pieces of a
pattern together into a coherent hole that is self-consistent. But
with a macro, all the programmer need do is call the name of the
design pattern and the parameters to it, and the macro-expader
automatically generates the whole self-consistent design pattern.
So from the view of the programmer, it's no longer a "pattern",
it's a utility.

Here's an example of what was originally a design pattern:
(unwind-protect
(let ((chan (open "foo.txt")))
(process-file chan))
(close chan))
The idea is that if something goes wrong while the file is open,
and PROCESS-FILE throws control out of the scope of its toplevel,
the CLOSE of the channel will nevertheless be executed, so the file
**will** end up closed before control reaches the main level of
program above this code.

So with a macro, it's no longer necessary to go to that pain of
writing all the stuff in just the right relationship. Now it's just:
(with-open-file (chan "foo.txt") (process-file chan))

Now that particular macro wasn't in older versions of Lisp before
Common Lisp, but the design pattern was so commonly useful that the
macro was incorporated into Common Lisp. But back in those earlier
versions of Lisp, programmers didn't have to write the
UNWIND-PROTECT pattern over and over. Instead they just wrote a
macro once and then called it. They didn't have to wait for Common
Lisp to do it. They could realize all by themselves that the design
pattern was common, and that the macro would condense the pattern
into a foolproof utility, so they just did it without waiting for
Common Lisp to provide it built-in. That's the value of
user-definable macros.

Now there are probably a *lot* of design patterns used in Common
Lisp and other languages which are not yet covered by pre-defined
macros or other mechanisms, because at the time Common Lisp or the
other laguages were standardized those patterns weren't recognized
as common enough in use with well-established macro that it seemed
reasonable to incorporate them into the standard language. After
time passes, in some problem domains it becomes common knowledge
that certain design patterns are common enough that it *really*
would be nice if the language supported them as a compact
expression *instead* of requiring the program to repeat the design
pattern over and over and over. Here is where languages such as
Lisp which have parse-tree macros, and other languages that don't
have them, differ: In Lisp, the appliation programmer has the power
to define a macro and thereby convert a design pattern into a
simple call. In languages without macros, the programmer does *not*
have that option, and must forever write out the entire design
pattern over and over and over until and unless the people defining
the standard and implement it decide to make that simple call part
of the language.

> OCaml has a full macro system but it is rarely used.

Is it a parse-tree macro system, like Lisp has, or is it a
string-syntax macro system, like C has? If the former, then why
isn't it used? Perhaps as I guessed above, because the parse tree
is so different from the source syntax that it's confusing to
switch back and forth between the two? If it's a string-syntax
macro system like C, then it's total crap and I can understand why
it's rarely used.

> F# has no macro system.

Then F# is crap compared to Lisp.

> Why do you think statically typed languages completely dominate
> general purpose programming?

Because a lot of people don't know any better and are stuck
with installed code base (legacy code) which must be maintained.

> > (The exception is in tight loops
> > where some machine type happens to work efficiently and where that
> > particular loop eats up most of the CPU time, which is precisely
> > where Lisp allows type declarations and consequent CPU-efficient
> > code as an *option* where it would actually be worth the cost.)
> Lisp is unable to regain the performance cost of its dynamic design:

As long ago as around the late 1970's, MacLisp achieved a
reputation that tight code loops with proper declarations ran
faster than the equivalent loop in Fortran, because the code within
each function was identical but Lisp had more efficient
function-call linkage. Do you dispute that, or do you claim that
Common Lisp has taken a step backward and no longer is as fast in
tight loops with proper declarations as it should be?

By the way, there *is* one apparent problem with Common Lisp:
Getting those declarations correct is a rather tricky skill, and
different implementations of Common Lisp present different levels
of difficulty for optimizing. See the two threads where I had
trouble optimizing a benchmark of random access within large array,
and where I had trouble optimizing a brute-force trial
factorization, in both cases it was doing CONSing when it shouldn't
have. (SBCL was able to avoid CONSing, but with the same source
code the very old version of CMUCL that my ISP has was doing lots
of CONSing.)

> >> finalizers,
> > You mean like to automatically close a system resource whenever the
> > Lisp hook for it is GC'd? Normally system resources like that are
> > lexically bound, whereby UNWIND-PROTECT is sufficient to make sure
> > they get closed when that block of code is exited. Is there
> > *really* a good reason to have such resources kept around per data
> > object instead of per block-entry/exit?
> Yes. Finalizers are useful for the same reason that GC itself is
> good (and Ada is bad): you don't want to have to shoehorn
> everything you do into scopes just to ensure that things get freed
> correctly.

On a philosophical level, I totally agree with you on this point.
I'm convinced you're not a troll!
Although you frequently go overboard on exaggerations in favor of
other languages and against Lisp, you *are* bringing up good issues
and occasionally (as here) actually showing some good insight.
So I'm not sorry I engaged you in debate.

Now let's discuss the practical matter, on a case by case basis:

If you are reading a file, you probably want only one agent reading
it at a time, and you probably want to process the contents of the
file straight through (or bulk copy the whole contents of the file
to a text-string and immediately close the file and process from
the string rather than from the file itself). Accordingly lexical
scope and UNWIND-PROTECT (such as in the WITH-OPEN-FILE macro) is
totally sufficient.

If you have a connection to a remote database, you may have
multiple agents sharing a single connection. But you want to close
the connection whenever the connection goes idle too long, and
automatically re-open it whenever somebody starts to use it again,
rather than keep it open yet idle the whole time anybody has a
handle on it. Accordingly a clock-timeout mechanism makes more
sense than a finalizer tied into the GC.

I'm hard pressed to think of any case where the best mechanism is a
GC-finalizer rather than lexical scope or clock-timeout and
auto-reopen. Please enlighten me with a few examples (no details,
just the essence of the problem to be solved) where GC-finalizer is
the *best* solution to resource management, OK?

> Another reason is that garbage collectors often free sooner than that.

I don't really believe that. Either the pointer to the handle to
the resource is lexically scoped, in which case GC won't reclaim it
until that pointer goes out of scope, in fact won't reclaim it
until sometime *after* the pointer goes out of scope; Or you have
multiple pointers being passed around in a non-lexical way and GC
won't reclaim it until the very last of those pointers is
inaccessible, whereas clock-timeout will close the resource as soon
as those pointers are no longer actively being used, hence again GC
will close the resource *after* the other method. Sure, once in a
while all the multiple pointers will suddenly go away at once, and
GC will by chance happen sooner than the idle-timeout. But other
times GC won't have yet happened when idle-timeout happens. So it's
a race that can go either way with no consistent winner. Please
present a scenerio where GC gets there *first* more often than not.
Or an example where idle-timeout is *not* an option, where the
resource absolutely must be kept active until the very last of the
references to it is gone and GC then can reclaim it, even if none
of those references are actively using it.

> > But if you can think of a really good use for random closure, I may
> > reconsider my opinion.
> I used finalizers in OCaml to collect unused OpenGL display lists and
> textures. The results were excellent: the code was greatly
> simplified at no cost.

Sorry, I don't know what those are, and they don't seem to be a
central issue that is worth my spending a half hour doing a Google
search to find a tutorial to teach me what they are. Would you
please define their important characteristics in regard to this
discussion, i.e. where they are physically located, what reason
they have for being kept around (until all references are gone),
why they can't be easily re-created if idle-timeout causes their
deletion but re-activity forces their re-existance, why they can't
be kept around nearly *forever* (until they have been idle a week
or two and it really would be surely a waste to keep them around
longer), etc.? On Unix/Linux there's a concept of various processes
in a tree, with each process dependent on the next parent process.
When a parent dies, all children automatically die. It would make
sense for a display list or texture to be a property of a
sub-process, which stays around so long as the main process is
around, then when the main process dies the sub-processes are
auto-killed and the display list or texture then disappears. Why
doesn't that model fit what's actually happening with display lists
and textures? The only reason I can think of is when the main
process is some persistent daemon, such as the Apache server. But I
don't think the Apache server or any of the other persistent
daemons are written in OCaml, so there must be some other reason
why that sub-process model wouldn't solve the problem
(hypothetically if OCaml didn't have finalizers, or if you decided
to translate your application to some other language that doesn't
have finalizers). So if you can succinctly enlighten me, please do.

> The vast majority of computers do not have virtual memory because
> they are embedded devices.

Yeah, that's a different environment from anything I've ever
programmed for. I would suspect that in an embedded device you
wouldn't want GC in the first place, you'd do better with mostly
static allocation with perhaps some non-cyclic dynamic structures
where reference count would work just fine. But it's late and maybe
I've lost track of the context.

> >> I think you should aspire to earn money directly from customers
> >> rather than asking people to give you money to develop your
> >> software.
> > I like that idea very much. Will you find me a potential customer
> > who wants some serverside application that doesn't already exist?
> You need to write a compelling sales page and make it indexable
> by search engines and make it as easy as possible for users to give
> you their money before plastering all of the news websites that you
> can find with descriptions of your new service and links to it.

That's a strawman comparison. In fact before doing *either*, I need
to find some customers who desire some new server-side application
that is within my capability, and who are willing to work with me
to define use cases that I might offer to implement, and then once
that's all done *then* we start talking about a fixed-price
contract to implement those use cases, and alpha testing, etc. Then
after I have one set of satisied customers, only then would it be
reasonble to advertise to see if there might be lots of other
customers for an existing customer-pleasing application.

So can you find me a first brainstorm-use-cases customer to get
this whole process started? I've mentionned lots of new software
technology I've implemented to the pointer where I feel they might
possibly be useful for a variety of applications that might be of
value to others. But until I find a potential customer who is
actually interested in what I have done and how I can work it into
a salable product, I don't know if any of the wondrous things I've
programmed really could lead to a salable product.

By the way, Tuesday of last week I gave a partial demo of some of
my computer-assisted instruction software, and the person seeing
the demo seemed to think it would be of interest to two of his
technical acquaintances, one very far away (about 1000 miles) and
one closer (about 40 miles), so he'll be contacting them to try to
get them interested.

Richard Heathfield

unread,
Jul 26, 2008, 5:06:27 AM7/26/08
to
Robert Maas, http://tinyurl.com/uh3t said:

>> >> Haskell, SML, OCaml, Mathematica, F# and Scala all allow real
>> >> problems to be solved much more concisely than with Lisp. Indeed, I
>> >> think it is difficult to imagine even a single example where Lisp
>> >> is competitively concise.
>> > What does "solved much more concisely" mean??
>> From: Jon Harrop <j...@ffconsultancy.com>
>> The solutions are shorter in modern FPLs.
>
> OK, let's say you have a text string which contains the notation
> for a nested list starting from the beginning of the string. How
> many lines of code, in various modern FPLs, would it take to parse
> that nested-list notation to produce an actual nested list
> structure, and also report back in another value the position in
> the string where the parse left off at the end of the nested-list
> notation? In Common Lisp it's just one line of code:
> (multiple-value-setq (ls ei) (read-from-string ts))
> where ts has the given text string,
> ls gets the resultant list structure,
> and ei gets the resultant end index.

In any (even relatively sensible) language where it takes /more/ than one
line of code, it only takes more than one line the /first/ time - because
the programmer will wrap it into a function (or procedure, or subroutine,
or whatever) and, in future, call the function (or whatever). So this
conciseness argument is a mere canard, as all program tasks eventually
boil down to:

call existing_solution

which is a one-liner in pretty well any language.

Pascal J. Bourguignon

unread,
Jul 26, 2008, 7:06:55 AM7/26/08
to
Richard Heathfield <r...@see.sig.invalid> writes:

> In any (even relatively sensible) language where it takes /more/ than one
> line of code, it only takes more than one line the /first/ time - because
> the programmer will wrap it into a function (or procedure, or subroutine,
> or whatever) and, in future, call the function (or whatever). So this
> conciseness argument is a mere canard, as all program tasks eventually
> boil down to:
>
> call existing_solution
>
> which is a one-liner in pretty well any language.

Not in languages that don't provide syntactic abstraction.

Let's take a simple example, the Memento pattern:
http://en.wikipedia.org/wiki/Memento_pattern

Everytime you want to apply this pattern to a new class, you have to
add the following methods and class, take care not to miss a member to
save/restore/keep in the memento:

public Object saveToMemento() {
System.out.println("Originator: Saving to Memento.");
return new Memento(state);
}
public void restoreFromMemento(Object m) {
if (m instanceof Memento) {
Memento memento = (Memento)m;
state = memento.getSavedState();
System.out.println("Originator: State after restoring from Memento: "+state);
}
}

private static class Memento {
private String state;

public Memento(String stateToSave) { state = stateToSave; }
public String getSavedState() { return state; }

In lisp, we write the same, but only ONCE:

(defgeneric save-to-memento (<base-class>)
(:documentation
"Returns a new <memento-class> holding all the state of <base-class>"))

(defmacro define-memento (<memento-class> <base-class> (&rest <base-class-slots>))
`(progn
(defclass ,<memento-class> ()
,(loop
:for slot :in <base-class-slots>
:collect `(,slot
:initarg ,(intern (symbol-name slot)
(load-time-value (find-package "KEYWORD"))))))
(defmethod save-to-memento ((self ,<base-class>))
(format *trace-output* "~&~S saving to memento.~%" self)
(make-instance ',<memento-class>
,@(loop
:for slot :in <base-class-slots>
:collect (intern (symbol-name slot) (load-time-value (find-package "KEYWORD")))
:collect `(slot-value self ',slot))))
(defmethod restore-from-memento ((self ,<base-class>) (memento ,<memento-class>))
(format *trace-output* "~&~S restoring from memento.~%" self)
,@(loop
:for slot :in <base-class-slots>
:collect `(setf (slot-value self ',slot) (slot-value memento ',slot)))
self)
',<memento-class>))


And then, everytime we have a new class:

(defclass originator ()
((state :initarg :state :accessor state)))
(defmethod (setf state) :before (new-value (self originator))