Oberon et al

Soren Renner — 2003-01-22 17:53:47

I am glad to see Oberon mentioned in Professor v. T's posts. I would like
to say again that I have written a little toy version of a Joy-like
language (called "toy"). I would not be indifferent if someone were to
ask to look at it. I am about to get a new box that can run Bluebottle (the
new Oberon-based OS, now completely open-source) and start programming
again. It would not be necessary, in order to show "toy" to one of you,
that he or she be running Bluebottle, or Native Oberon as an OS. LNO (Linux Native Oberon) would be sufficient.

To switch away from C as the ground upon which the programming language
castle is erected is a major step. But I think it is necessary. At least,
I cannot believe otherwise, or act as though I did. Building a full
Joy version in Oberon would be a large project, but one I could tackle,
I think. If I have struck a chord in any of you, however faint,please
listen to it. I do not ask that you switch to Oberon for your own
programming, or that you become advocates; only that some one of you
(preferably, of course, Manfred van Thun) installs LNO, NO, or Bluebottle
for the purpose of looking at what I have done, and what I will (given
minimal encouragement) do. Thank you in advance,

Soren Renner

Manfred von Thun — 2003-01-24 02:16:23

On Wed, 22 Jan 2003, Soren Renner wrote:

> To switch away from C as the ground upon which the programming language
> castle is erected is a major step. But I think it is necessary. At least,
> I cannot believe otherwise, or act as though I did. Building a full
> Joy version in Oberon would be a large project, but one I could tackle,
> I think.

I would urge you to work on a small version first. You might look at
the main Joy page, Part 3 "Miscellaneous", has links to a mini-Joy
written in Pascal, 550 lines or so. Could give you some useful hints.
You won't need the garbage collector, of course. Also, this implementa
tion needs to read the library in two passes: once for the names only,
and once for the actual definitions. I would not do that again.

When you have written a small version, I'd be glad to put it together
with a demo input and output in the same "Miscellaneous" collection.

Then start afresh on a fuller version.

> some one of you
> (preferably, of course, Manfred van Thun) installs LNO, NO, or Bluebottle
> for the purpose of looking at what I have done, and what I will (given
> minimal encouragement) do. Thank you in advance,

Well, I don't know what these things are, how big they are, and how
portable they are. They might need to be put into the concatenative
files area. Or just a link from the Joy page to their home. We'll see when
the time comes.

But do a little version first, with a demo.

- Manfred

falvosa <kc5tja@arrl.net> — 2003-02-28 07:52:56

> > (preferably, of course, Manfred van Thun) installs LNO, NO, or
Bluebottle
> > for the purpose of looking at what I have done, and what I will
(given
> > minimal encouragement) do. Thank you in advance,
>
> Well, I don't know what these things are, how big they are, and how
> portable they are. They might need to be put into the concatenative
> files area. Or just a link from the Joy page to their home. We'll
see when
> the time comes.

It's been 3 years since I last was here, and a lot has happened. I'm
still catching up.

Oberon is a Pascal-like programming language, with garbage collection,
run-time performance just a hair short of C (because of run-time
checks; if you turn off the type safety and run-time checks, the code
is as fast as C, easily), and a HECK of a lot easier to understand
syntax.

LNO is the Linux Native Oberon package. It's an implementation of
Oberon System 3 for Linux, that runs under X11.

However, you don't necessarily need to use the Oberon System to
program in Oberon. For example, I write useful Linux software using
oo2c, a stand-alone Oberon environment for Linux that produces native
Linux executables. If the Oberon System and its user interface is
off-putting to you, then oo2c sounds like a better solution.

The sole disadvantage of Oberon for the time being is that it lacks a
truely standard run-time library (e.g., it has no stdio equivalent).
The Oakwood conventions are supposed to fill that role, and largely do
for most programs, but it's not complete. Moreover, environments such
as OO2C will sometimes elect to use different module names for the
Oakwood components. For example Oakwood's Out module is OakOut in
OO2C. Fortunately, porting is very, very easy -- you can use module
aliases:

IMPORT Out := OakOut, ..etc...

Despite the incompleteness of the Oakwood modules, they should be way
more than necessary to implement a language with, especially for Joy.

--
Samuel A. Falvo II

phimvt@lurac.latrobe.edu.au — 2003-03-12 05:14:16

On Fri, 28 Feb 2003, falvosa <kc5tja@...> wrote:

[.. apropos writing a Joy in Oberon ..]

> Oberon is a Pascal-like programming language, with garbage collection,
> run-time performance just a hair short of C (because of run-time
> checks; if you turn off the type safety and run-time checks, the code
> is as fast as C, easily), and a HECK of a lot easier to understand
> syntax.

Coming from Wirth, it just has to be a very clean language. As you
know, I have programmed for years in Pascal.

> LNO is the Linux Native Oberon package. It's an implementation of
> Oberon System 3 for Linux, that runs under X11.

I take it that this means not many potential Joy users could
switch to this system so easily.

> However, you don't necessarily need to use the Oberon System to
> program in Oberon. For example, I write useful Linux software using
> oo2c, a stand-alone Oberon environment for Linux that produces native
> Linux executables. If the Oberon System and its user interface is
> off-putting to you, then oo2c sounds like a better solution.

On the other hand, this would be far more attractive for potential
Joy users. Still, even getting the compiler running would seem
to be no mean feat. I also noticed that it uses the same Boehm
garbage collector as John Cowan's Joy interpreter does. At any
rate, the Oberon compiler would be several orders of magnitude
larger than the Joy interpreter

[..more on Oberon implementations..]

Thank you for all this, especially the oo2c implementation was quite
new to me.

I do look forward to seeing and at least reading a Joy
interpreter written in Oberon.

- Manfred

Samuel Falvo — 2003-03-12 09:18:57

> Coming from Wirth, it just has to be a very clean language. As you
> know, I have programmed for years in Pascal.

I haven't. Personally, I can't stand Pascal as a language. I can't put my
finger on it, but it seems very limiting to me. I've never had any experience
with Modula-2, but from what I've seen of it, it looks like it was much, much
better. But trying Oberon was a major eye-opener for me, and I will try hard
to write my future software in either Forth or Oberon whenever possible.

I was even considering developing a shareware application for PalmOS using
Oberon, though that will require a custom-made compiler. I was going to have
it compile to a set of bytecodes optimized for run-time native code generation
for best speed, with the core virtual machine itself written in Forth. This
would enable the same binary modules to run either on x86 or PalmOS, with full
data interoperability as well.

However, Forth itself is also a candidate for this task. However, I'm also
concerned that people will be discouraged from writing plug-in modules for the
product if I were to write it in Forth... :/

> > LNO is the Linux Native Oberon package. It's an implementation of
> > Oberon System 3 for Linux, that runs under X11.
>
> I take it that this means not many potential Joy users could
> switch to this system so easily.

I don't understand what you're trying to convey here. Can you clarify?

--
Samuel A. Falvo II


__________________________________________________
Do you Yahoo!?
Yahoo! Web Hosting - establish your business online
http://webhosting.yahoo.com

John Cowan — 2003-03-12 17:38:15

Samuel Falvo scripsit:

> I haven't. Personally, I can't stand Pascal as a language. I can't put my
> finger on it, but it seems very limiting to me.

See Brian Kernighan's "Why Pascal Is Not My Favorite Programming Language"
at http://www.lysator.liu.se/c/bwk-on-pascal.html among other places.

--
John Cowan jcowan@... www.reutershealth.com www.ccil.org/~cowan
Promises become binding when there is a meeting of the minds and consideration
is exchanged. So it was at King's Bench in common law England; so it was
under the common law in the American colonies; so it was through more than
two centuries of jurisprudence in this country; and so it is today.
--_Specht v. Netscape_

Samuel Falvo — 2003-03-12 19:49:42

> > I haven't. Personally, I can't stand Pascal as a language. I can't put my
> > finger on it, but it seems very limiting to me.
>
> See Brian Kernighan's "Why Pascal Is Not My Favorite Programming Language"
> at http://www.lysator.liu.se/c/bwk-on-pascal.html among other places.

I have, but his views are not only orthogonal to mine, but even somewhat
offensive, because he leaves it at that. He never considers Modula-2 nor
Oberon as alternative programming languages, which leads me to believe that his
feelings towards these languages are precisely the same as for Pascal, which is
patently false.

Also, if I remember correctly, he brings up a number of stylistic issues (which
do apply to Modula-2 and Oberon) which I do not agree with philosophically,
especially in light of studies conducted comparing C to Pascal-like languages
and my own personal experiences. Many of the issues he claims as advantages
have been proven to be major impediments to C and, in particular, maintenance
of programs written in C. As a programmer who's worked with C for almost two
decades, I've been bitten by C, and even its successor C++, more times than I
can remember. When I discovered Oberon for the first time, I had a bit of a
love/hate relationship because I wasn't used to its *extremely* strong
type-checking. But now I can't think of any other way I'd like to program. I
still love Forth, but I sure wish it had type checking at least as strong as
Oberon's now. In fact, come to think of it, Oberon is simple enough a language
to actually implement in-place in Forth itself. I might consider playing
around with this concept in the future; it could actually be the solution to my
third-party plug-in developers dilemma.

--
Samuel A. Falvo II


__________________________________________________
Do you Yahoo!?
Yahoo! Web Hosting - establish your business online
http://webhosting.yahoo.com

John Cowan — 2003-03-12 20:52:40

Samuel Falvo scripsit:

> I have, but his views are not only orthogonal to mine, but even somewhat
> offensive, because he leaves it at that. He never considers Modula-2 nor
> Oberon as alternative programming languages, which leads me to believe that his
> feelings towards these languages are precisely the same as for Pascal, which is
> patently false.

Note the date. Kernighan's review was written in 1981, at which time Modula-2
was one year old and Oberon did not exist.

> I still love Forth, but I sure wish it had type checking at least as strong as
> Oberon's now.

Check out strongForth at http://home.t-online.de/home/s.becher/forth/
I don't know much about it, but it looks like what you want.

--
Schlingt dreifach einen Kreis vom dies! || John Cowan <jcowan@...>
Schliesst euer Aug vor heiliger Schau, || http://www.reutershealth.com
Denn er genoss vom Honig-Tau, || http://www.ccil.org/~cowan
Und trank die Milch vom Paradies. -- Coleridge (tr. Politzer)

wtanksleyjr@cox.net — 2003-03-12 22:07:19

From: Samuel Falvo <falvosa@...>
>Oberon's now. In fact, come to think of it,
>Oberon is simple enough a language to actually
>implement in-place in Forth itself.

It's worth noting that a nice subset of Oberon is
distributed with the parser generator "Gray" (or
was that "Grey", I can never remember), which is
written in and for Forth. Since you're writing your
own Forth, you could easily engineer Gray alongside
your Forth to be able to help you output code.

Concatenative folks: Gray is interesting in that it
uses Forth's ability to introspect the currently-active
source code. Thus, you can define a parser and directly
use it in the same source file in which it was defined.
This ability to introspect currently active source is
not entirely unique to concatenative languages (Lisp has
it as well), but it's only possible to the extent that
a language is NOT applicative. That is, you can only
plausibly parse source code which hasn't yet been parsed
by the compiler, which means that a function can't parse
its own arguments. Lisp gets around this by defining three
different types of functions: ordinary, macros, and reader
macros. Forth only needs two types to handle all the same
things.

It's interesting that although concatenativity allows
this introspection, actually _doing_ the
introspection destroys concatenativity, since it makes
the function depend on what appears after it in the
source text. It's therefore very interesting that
common practice in Forth frowns upon using the
introspection capabilities.

>Samuel A. Falvo II

-Billy

Samuel Falvo — 2003-03-12 22:31:12

> Note the date. Kernighan's review was written in 1981, at which time
> Modula-2
> was one year old and Oberon did not exist.

Exactly my point. He has failed to revisit his position with respect to this,
which leaves a nasty aftertaste in my mouth. Most authors who publish such
opinion pieces usually try to periodically update their experiences and report
on them. For example, Frederick Brooks, upon republishing the Mythical Man
Month, has a whole new section in the book on his experiences since the first
publication. Many of his ideas have been confirmed, many have been proven
false, and he reports on them accordingly. And then there's me -- I really
dislike Pascal, but I absolutely love Oberon.

I'd like to see Kernighan do the same with respect to Oberon.

> > I still love Forth, but I sure wish it had type checking at least as strong
> as
> > Oberon's now.
>
> Check out strongForth at http://home.t-online.de/home/s.becher/forth/
> I don't know much about it, but it looks like what you want.

The heart is in the right place, but the mechanism and details aren't quite
there yet. Billy Tanksley and I are constantly discussing this Forth
implementation, its limitations, and its advantages. Its biggest limitation is
its lack of support for aggregate data types and dynamic memory management
(automatically managed or otherwise).

What I'd like to see is a datatyping mechanism that borrows heavily from
Haskell/ML data types, since they are the simplest typing system I've ever seen
(though it takes a while to wrap your mind around them, as it did for me). For
example, given a hypothetical infix functional language:

datatype SceneList =
EmptyList
| SceneNode( object: SceneObject; next: SceneList );

datatype SceneObject =
EmptyScene
| AggregateObject( left, top: integer; children: SceneList )
| Label( left, top: integer; text: String )
| Line( left, top, right, bottom: integer )
| Box( left, top, width, height: integer );

The single statement above creates a whole type hierarchy, without all the
overhead of a full-blown object run-time environment. The types declared are,
of course, themselves constructor functions.

A constructor function is defined as a function that assembles, or constructs,
its parameters into a single object instance. A decomposer (to distinguish it
from the overloaded term destructor) does the reverse. So, for example, to
build a scene graph using applicative notation, we can do this:

LET myLabel := Label( 0, 0, "Hello world!" ) IN
myGraph := AggregateObject( 0, 0,
SceneNode( myLabel,
SceneNode( Box( -2, -2, Width(myLabel)+4, Height(myLabel)+4 ),
EmptyList )));

It is interesting to note that the compiler, despite the applicative notation,
knows entirely how to construct this structure statically in memory, because it
knows what a constructor is versus a normal function. Width() and Height()
would presumably be functions that can execute at compile-time, if enough
compile-time knowledge was known. If not, the statement would have to be done
at run-time. But, I digress.

In most applicative languages, the decomposer also has the same name as its
constructor. So something like this:

Width( Label( leftEdge, topEdge, string ) ) := FONT_WIDTH * lengthOf( string
);

lets the function Width access individual elements of the object. Most
languages even support a don't care construct:

Width( Label( _, _, string ) ) := FONT_WIDTH * lengthOf( string );

My "Ideal Forth" version of this would construct the scene graph in a manner
not unlike the following:

EmptyList ( e )
S" Hello world!" 0 0 Label ( e label )
DUP DUP Width SWAP Height -2 -2 2SWAP Box ( e label box )
>R SceneNode R> ( node box )
SceneNode ( node )
0 0 AggregateObject ( agg )

: Width ( label -- width )
~Label NIP NIP LengthOf FONT_WIDTH * ;

Note how I use ~Label as the word for decomposing Label.

My proposed syntax doesn't address how to declare the existance of these types
either. I'm still pondering over that, but this is what I have so far:

TYPE SceneList
TYPE EmptyList --> SceneList
TYPE SceneNode --> SceneList

<< SceneObject SceneNode >> SceneNode

TYPE SceneObject
TYPE EmptyScene --> SceneObject
TYPE AggregateObject --> SceneObject
TYPE Label --> SceneObject
TYPE Line --> SceneObject
TYPE Box --> SceneObject

<< INT INT SceneList >> AggregateObject
<< INT INT STRING >> Label
<< INT INT INT INT >> Line
<< INT INT INT INT >> Box

Here, TYPE defines a new type descriptor, a constructor primitive, AND a
decomposer primitive. This requires that the host Forth environment support
programmatically created words at run-time (which none that I'm aware of do;
the closest you can get is very creative use of EVALUATE in ANSI Forth). -->
takes the last defined type and updates its "is-a" pointer to point to the
specified data type. << is a word that interprets the given type ID list to
build the "contents" of the data type (>> is not an actual Forth word, but
instead just a delimiter, similar to -- in Becher's StrongForth's type
signatures), then parses the next word to update the relavent type descriptor's
"contents" field. INT and STRING are special type descriptors that represent
primitive data types (non-garbage collectable).

It's still a bit verbose for me, but it establishes everything I could need for
a good type system. My big quandary is that what appears between << and >>
aren't self-documenting, though the remainder of it is nicely telegraphic for
me. It does support, however, efficient garbage collection, since even a naive
Forth compiler will then know which fields are or are not references to other
objects at the time the type descriptors are laid down in memory.

As a possible syntactical optimization, we can have << also define its own
type. For example:

TYPE SceneList
<< >> EmptyList --> SceneList
<< SceneObject SceneList >> SceneNode --> SceneList

I think this is too visually cluttered though. It also prevents the use of
forward-declared types to handle circular references (e.g., where a SceneNode
references a SceneObject, and that same SceneObject references a SceneNode. In
this case, you must TYPE both before you declare the contents of each type).

Yes, I realize that TYPE is already allocated for use in Forth. But I didn't
want to constantly type DATATYPE throughout the whole e-mail. :)

--
Samuel A. Falvo II


__________________________________________________
Do you Yahoo!?
Yahoo! Web Hosting - establish your business online
http://webhosting.yahoo.com

Steven Shaw — 2003-03-13 12:19:56

> See Brian Kernighan's "Why Pascal Is Not My Favorite Programming Language"
> at http://www.lysator.liu.se/c/bwk-on-pascal.html among other places.

Thank goodness many of the criticisms levelled against Pascal don't apply to
successors like Modula-2, Modula-3, Oberon, Oberon-2, and Component Pascal.

Even Brian's involved with a new programming language which borrows from
Pascal: http://www.vitanuova.com/inferno/papers/descent.html. So it couldn't
have been all that bad :-).

Steve.

John Cowan — 2003-03-13 13:57:52

Steven Shaw scripsit:

> Even Brian's involved with a new programming language which borrows from
> Pascal: http://www.vitanuova.com/inferno/papers/descent.html. So it couldn't
> have been all that bad :-).

In fact, his involvement apparently is limited to writing that paper, and
the Pascal influence is limited to ":=" for assignment and the syntax of
declarations.

--
John Cowan http://www.ccil.org/~cowan jcowan@...
Please leave your values | Check your assumptions. In fact,
at the front desk. | check your assumptions at the door.
--sign in Paris hotel | --Cordelia Vorkosigan

Steven Shaw — 2003-03-13 16:19:20

> > Even Brian's involved with a new programming language which borrows from
> > Pascal: http://www.vitanuova.com/inferno/papers/descent.html. So it
couldn't
> > have been all that bad :-).
>
> In fact, his involvement apparently is limited to writing that paper, and
> the Pascal influence is limited to ":=" for assignment and the syntax of
> declarations.

A crucial pascalism :-).

Samuel Falvo — 2003-03-13 20:58:21

> Even Brian's involved with a new programming language which borrows from
> Pascal: http://www.vitanuova.com/inferno/papers/descent.html. So it couldn't
> have been all that bad :-).

I wasn't aware of this! I will have to check this out.

--
Samuel A. Falvo II

__________________________________________________
Do you Yahoo!?
Yahoo! Web Hosting - establish your business online
http://webhosting.yahoo.com

Samuel Falvo — 2003-03-13 22:36:17

> > Even Brian's involved with a new programming language which borrows from
> > Pascal: http://www.vitanuova.com/inferno/papers/descent.html. So it
> couldn't
> > have been all that bad :-).
>
> In fact, his involvement apparently is limited to writing that paper, and
> the Pascal influence is limited to ":=" for assignment and the syntax of
> declarations.

Oh man ... oh man ... I don't know what to make of this language. It
apparently combines the worst syntactical elements of C and the worst
syntactical elements of Haskell (actually CSP, from which Haskell is also
derived from), and mish-mashes it all together into a language where even the
Hello World example on the page leaves the reader curious as to when the pain
and suffering will end.

That is an abysmal programming language. I'll take ActiveOberon or just plain
Haskell over this *any* day of the week.

Wow.

--
Samuel A. Falvo II


__________________________________________________
Do you Yahoo!?
Yahoo! Web Hosting - establish your business online
http://webhosting.yahoo.com

phimvt@lurac.latrobe.edu.au — 2003-03-14 05:38:37

On Wed, 12 Mar 2003, Samuel Falvo wrote:

> > Coming from Wirth, it just has to be a very clean language. As you
> > know, I have programmed for years in Pascal.
>
> I haven't. Personally, I can't stand Pascal as a language. I can't put my
> finger on it, but it seems very limiting to me. I've never had any experience
> with Modula-2, but from what I've seen of it, it looks like it was much, much
> better. But trying Oberon was a major eye-opener for me, and I will try hard
> to write my future software in either Forth or Oberon whenever possible.

When I started teaching myself programming, we had a DEC10 and as
languages we had Fortran, Cobol, Basic, Focal, Algol60, Snobol, Lisp,
Bliss and an early Pascal. After a very brief exposure to Basic and Focal
and a quite lengthy one to Algol60, I got hooked on Pascal in part
because DEC was using it as their systems programming language. And
indeed they did provide an excellent Pascal compiler which implemented
*REAL* Pascal. I soon learned how to use local procedures as parameters
in recursive calls to provide continuations in backtracking programs.
Our Computer Science Department was at that time running a Pyramid
and later a Sun, and they were teaching Pascal on those. All my
backtracking programs did work on those machines too. When I was
asked to do several years of teaching for them, I was able to use
Pascal throughout. But often my students complained "it does not work
on my micro.." Well, the cheap Pascals never bothered about maintaining
the static links (or displays) to pass on local environments.

Indeed, the use of local procedures as parameters in recursive
calls appears to be a sort of black art, and I seem to be one
of its few practitioners. See Chapter 8 "Second Order Recursion"
on my Pascal page:
http://www.latrobe.edu.au/philosophy/phimvt/s00bok.html
and then Chapters 9 10 11 15 16 20 for some applications.

I did note that the p2c utility cannot handle this sort of thing,
and I am told that Modula2 does not allow it either, and as
far as I can tell, Oberon also does not allow it. But I am
heartened to hear that Modula3 does allow it, and the new Gnu
Pascal also does. I don't know about the newer Oberons.

But all this is quite irrelevant for an implementation of
Joy in Oberon, because there is no backtracking involved.
However, if anybody is thinking about a Joy-ified Prolog,
do keep this backtracking technique in mind.

[.. a propos LNO, the Linux Native Oberon 3 to run under X11 ..]

> > I take it that this means not many potential Joy users could
> > switch to this system so easily.
>
> I don't understand what you're trying to convey here. Can you clarify?

I meant something very simple actually, sorry if it wasn't clear.
Many users either have C already or at least can get one easily
and it will run on anything. Unless I misunderstand what you
said, many potential users could not switch to Linux under X11
- because they are running something else, or they have no
control over what operating system is running.

- Manfred

Samuel Falvo — 2003-03-14 07:53:09

> I did note that the p2c utility cannot handle this sort of thing,
> and I am told that Modula2 does not allow it either, and as
> far as I can tell, Oberon also does not allow it. But I am
> heartened to hear that Modula3 does allow it, and the new Gnu
> Pascal also does. I don't know about the newer Oberons.

I am not certain that Oberon forbids passing local procedures as parameters.
If it's used purely within the scope of its containing procedure, it should be
valid to do so.

However, what might be illegal is passing a reference to an inner procedure to
ANOTHER outer procedure. This would crash the system if it were allowed to
happen, because the stack frames would not be properly constructed on the call
stack.

> But all this is quite irrelevant for an implementation of
> Joy in Oberon, because there is no backtracking involved.
> However, if anybody is thinking about a Joy-ified Prolog,
> do keep this backtracking technique in mind.

I have implemented backtracking in Forth using very few lines of code. Forth
makes this possible because it exposes the return stack as a first-class,
programmer-accessible object.

> I meant something very simple actually, sorry if it wasn't clear.
> Many users either have C already or at least can get one easily
> and it will run on anything. Unless I misunderstand what you
> said, many potential users could not switch to Linux under X11
> - because they are running something else, or they have no
> control over what operating system is running.

This is what I *thought* you meant, but I wasn't quite sure. To this, I agree.
Nonetheless, oo2c is, or at least should be, relatively platform independent
-- it is an Oberon to C compiler, which has the convenience of automatically
invoking the C compiler for you.

Also, Oberon System 3 is also available for Windows and several other
platforms. There's also an Oberon V4, and now there's Bluebottle, which is an
implementation of ActiveOberon. I'm not sure if Bluebottle is ported to
Windows or Linux, but I do know that it runs on x86 PCs natively as its own
operating system.

--
Samuel A. Falvo II


__________________________________________________
Do you Yahoo!?
Yahoo! Web Hosting - establish your business online
http://webhosting.yahoo.com

John Cowan — 2003-03-14 12:01:47

phimvt@... scripsit:

> Indeed, the use of local procedures as parameters in recursive
> calls appears to be a sort of black art, and I seem to be one
> of its few practitioners.

In Pascal, I suppose you mean. Of course the entire Lisp (and especially
Scheme) community practices this art with abandon, and doesn't consider
it black. In Scheme, furthermore, you can return a local procedure as
the value of one of its enclosing procedures.

The GNU dialect of C allows local procedures and procedure arguments,
but ANSI C does not.

--
John Cowan http://www.ccil.org/~cowan cowan@...
To say that Bilbo's breath was taken away is no description at all. There
are no words left to express his staggerment, since Men changed the language
that they learned of elves in the days when all the world was wonderful.
--_The Hobbit_

John Cowan — 2003-03-14 12:03:27

Samuel Falvo scripsit:

> I have implemented backtracking in Forth using very few lines of code. Forth
> makes this possible because it exposes the return stack as a first-class,
> programmer-accessible object.

I think, though, that that is not ANS FORTH compliant; no non-primitive
word can have a net effect on the return stack. In particular, I am
certain that the idiom "r> drop exit" is not ANS FORTH.

--
John Cowan http://www.ccil.org/~cowan cowan@...
To say that Bilbo's breath was taken away is no description at all. There
are no words left to express his staggerment, since Men changed the language
that they learned of elves in the days when all the world was wonderful.
--_The Hobbit_

Samuel Falvo — 2003-03-14 16:41:46

> In Pascal, I suppose you mean. Of course the entire Lisp (and especially
> Scheme) community practices this art with abandon, and doesn't consider
> it black. In Scheme, furthermore, you can return a local procedure as
> the value of one of its enclosing procedures.

While this may be true, it's generally not advocated in Lisp programming. Lisp
programmers generally tend to stick with more traditional, functional
programming techniques, while Scheme programmers are more cutting edge.
Indeed, Scheme came about primarily because they were unhappy with the
doggedness of the Lisp programming community.

The other thing to consider is that Lisp and Scheme uses a form of local
variable binding where returning a function is like returning an object with a
single method (a closure). It's not lexical binding, I think they call it
dynamic binding or something like that. Anyway, Oberon uses more traditional,
lexical binding. I'm rather surprised to hear that Pascal uses dynamic
binding, for otherwise it could not support passing inner procedures as
parameters to other outer procedures, and actually have the system work.

--
Samuel A. Falvo II


__________________________________________________
Do you Yahoo!?
Yahoo! Web Hosting - establish your business online
http://webhosting.yahoo.com

Samuel Falvo — 2003-03-14 17:25:20

> I think, though, that that is not ANS FORTH compliant; no non-primitive
> word can have a net effect on the return stack. In particular, I am
> certain that the idiom "r> drop exit" is not ANS FORTH.

Without giving you exceedingly uninteresting details, my feelings towards ANSI
Forth are not favorable. ANSI Forth made a lot of decisions that lean towards
very conventional methods of programming which patently do not take advantage
of Forth's unique run-time environment, all in the name of some nebulous
concept called portability.

It is unfortunately true that Chuck Moore was right all these years:
portability *is* a myth. One need not look any further than C, supposedly the
most portable of all languages judging by its popularity, for ample proof.
Many "portable" C programs are literally riddled with #ifdef statements to
perform conditional compilation. You often see things like the following:

#ifdef __X86__
extern void DoBlort_x86( ... );
#elsif __ARM__
extern void DoBlort_arm(...);
#elsif __MIPS__
extern void DoBlort_mips( ... );
#endif

The above code fragment represents some of the most readable examples of how C
achieves its platform independence. A look through any Linux source code file
will show much more realistic, and hairy, examples. Getting back to the
example, what if I don't use an 80x86, ARM, or MIPS processor? What if I use a
PowerPC instead? I'm screwed -- the functionality for that function hasn't
been defined. So much for C's platform independence. While C, as a language,
is portable between platforms, the software you write in it certainly isn't.
However, this is also true of Forth!

A better solution would be to replace all of the above with a single
declaration:

extern void DoBlort(...);

and have three independent source modules that all expose the same interface,
one for x86, ARM, and MIPS. Of course, it doesn't solve the platform
independence issue, but it does certainly improve code readability and
maintainability. Furthermore, creation of a PowerPC-version of DoBlort no
longer involves changing every header file or source file that references
DoBlort in conditional compilation blocks.

This is an example of achieving "portability through factoring," a concept that
Moore has adhered to since the earliest days of Forth. The equivalent to the
above practice in Forth would be to heavily factor your software. Stick to the
dictum, "One word, one line," like it was a religious mandate. It is
unfortunate that 90% of the people who preach this *do not follow it
themselves.* For example, Wil Baden (IIRC), someone very well respected in the
Forth community for his engineering prowess and knowledge of Forth, posted some
code that performed random number generation using the Mersenne Twister. ONE
definition, 27 lines, using absolutely NO factorization at all. The result was
a definition that used gratuitous quantities of single-letter variable names
(whose purposes are still unknown to me), and the reader is left wondering,
"What the heck does all this do? How do these parts interact with each other?"
It was classic Forth write-only fare.

I have written many small- to medium-sized tools in Forth. Billy can attest to
my code's high degree of readability, and indeed, even portability. I make
*zero* attempts to write "portable" Forth, choosing instead to use
portability-by-factoring. A small example would be my metronome program, which
under PygmyForth, talks to the PC's speaker hardware directly, but under
GForth, uses ioctls on stdout to affect the PC speaker (it will not work under
an xterm though). Another example would be my VIBE VI-like Block Editor.
Here, over 40K of source code, mostly one-word-one-line, was initially written
under GForth, making use of run-time dictionary searches using FIND and similar
words to achieve extendability by the programmer. In porting it to Pygmy
Forth, which is patently not ANSI, I spent no more than an hour and a half
writing the relavent words (which fits all on one screen, btw), and most of
that time was spent perusing Pygmy's source to find the best way to approximate
ANSI behavior. The end result is no loss of functionality, and the isolation
of words that are likely to give problems when porting it to some other
non-ANSI environment, so porting it will be even easier then -- I know what to
look for in advance.

Remember that Forth, and other concatenative languages, do NOT use local
variables; therefore, you must make interactions within a word explicit by
explaining, using the source code itself, WHAT is trying to be done, not HOW.
This can only be done using telegraphic names and highly factored code. *This
is the concatenative advantage!* Note that this should *especially* be
standard practice for Joy programs, because Joy, unlike Forth, can pre-parse
the whole source before emitting code. As a consequence, Joy compilers can
tell whether or not a word is used only once or twice throughout the whole
module, and thus, cleanly inline its definition where appropriate. This
eliminates the subroutine call overhead that is a perpetual problem in most
Forths (and is most likely why people don't factor in Forth; however, all the
commercial Forth vendors today produce Forths that do inline single- or
double-use words). However, even in Forth, I find subroutine overhead to be
inconsequential, except in the inner-most, highly numerically intensive, loops.
We're using 1GHz or more processors today; surely, a maximum 20 cycle penalty
for subroutine invokations is tolerable, and that's only when the branch was
mis-predicted. It's substantially lower if the call was successfully
predicted.

While I'm rarely in total agreement with Jeff Fox, I have to concur with his
assessment on c.l.f that ANSI Forth promulgates the "C" way of doing things in
Forth. While ANSI has its high points, making the return stack effectively
off-limits as a first-class, programmer-accessible object has done much to
limit the expressive power of Forth. In most circumstances, people generally
don't notice, because they're not familiar with, or even afraid of, using
advanced control flow techniques such as backtracking. However, use of
backtracking and highly telegraphic word names can make *extremely*
maintainable source code in Forth:

: DoSomething 0 50 ForAllIntegers: OnlyEvenIntegers: Blah ;

This technique becomes especially powerful when you desire to factor out common
code among a number of loops. Without this technique, you cannot create the
equivalent of a Haskell "generator," which is instrumental in performing such a
factorization. As a result, code becomes harder to maintain, harder to test,
and significantly harder to read. I posted an example of a Mersenne Twister
implementation in c.l.f one day, and it had three DO/LOOP constructions, ALL of
which were nearly identical, and could have easily been factored out had ANSI
Forth supported operations on the return stack.

Notice how most Forth implementations simply lack the vocabulary to treat the
dictionary as a true, first-class object as well. You can create new words
only by using CREATE or : (VARIABLE and CONSTANT are defined in terms of
CREATE), both of which work by taking their input NOT from the program, but
from the current input stream. Also note that there is no way to iterate
"portably" over the contents of the dictionary. This is directly attributed to
the lack of support for defining new control flow structures in Forth: had ANSI
Forth supported backtracking, iterating over the dictionary *portably* would be
a reality, even if it wasn't provided as standard equipment.

As a result of all these mishaps, I'm forced, as an ever increasing number of
others are, to make non-ANSI compliant Forth environments. non-ANSI
environments are still very popular these days, ironically, because of ANSI
Forth itself.

--
Samuel A. Falvo II


__________________________________________________
Do you Yahoo!?
Yahoo! Web Hosting - establish your business online
http://webhosting.yahoo.com

Ivan Tomac — 2003-03-15 13:21:00

--- In concatenative@yahoogroups.com, Samuel Falvo <falvosa@y...>
wrote:
> > I think, though, that that is not ANS FORTH compliant; no non-
primitive
> > word can have a net effect on the return stack. In particular,
I am
> > certain that the idiom "r> drop exit" is not ANS FORTH.
>
> Without giving you exceedingly uninteresting details, my feelings
towards ANSI
> Forth are not favorable. ANSI Forth made a lot of decisions that
lean towards
> very conventional methods of programming which patently do not
take advantage
> of Forth's unique run-time environment, all in the name of some
nebulous
> concept called portability.
>
> It is unfortunately true that Chuck Moore was right all these
years:
> portability *is* a myth. One need not look any further than C,
supposedly the
> most portable of all languages judging by its popularity, for
ample proof.

C isn't the most portable of languages by far. The reason it's so
popular has nothing to do with it's portability - the portability is
the result of standardization. These days most languages with well
defined and recognized standards are at least as portable as C.
Being a relatively low-level language, C isn't really all that
portable by nature just like Forth. Portability in C and Forth is
mostly enforced through standards.

Portability does not have to be a myth though. It could be maximised
by removing all system specific types from language (i.e. ints,
floats, etc.) and replacing them with something more generic. Of
course such a language would probably be fairly inefficient compared
to C even with optimizing compilers but I wouldn't think the
performance would be much worse then that of most other functional
languages (which is still pretty bad though).

I'm currently working on something like this although not for the
sake of portability, portability just happens to be a side-effect.
Thanks to some of the feedback, comments and discussions I've
gathered from this board in the past mainly from Billy Tanksley,
Manfred Von Thun and Brent Kirby I've completely redesigned my
language from scratch (at the same time it's also starting to look a
lot more like a typeless version of Joy instead of a functional
version of Forth). I still have the old Forth-like version though.

> *snip*
>
> A better solution would be to replace all of the above with a
single
> declaration:
>
> extern void DoBlort(...);
>
> and have three independent source modules that all expose the same
interface,
> one for x86, ARM, and MIPS. Of course, it doesn't solve the
platform
> independence issue, but it does certainly improve code readability
and
> maintainability. Furthermore, creation of a PowerPC-version of
DoBlort no
> longer involves changing every header file or source file that
references
> DoBlort in conditional compilation blocks.
>

Not really a better solution - that's what #ifdefs mostly do: you
end up with a bunch of different versions of DoBlort() for different
processors. It does improve readability a bit though.
Depending on the code and how well it's written you might not have
to change all that many header files either. However trying to
understand what those header files do might be a bit tough with all
those conditional defines.

> This is an example of achieving "portability through factoring," a
concept that
> Moore has adhered to since the earliest days of Forth. The
equivalent to the
> above practice in Forth would be to heavily factor your software.
Stick to the

IIRC Chuck Moore advocates rewriting a program for every new system
instead of porting an existing program.

> *snip*
>
> Notice how most Forth implementations simply lack the vocabulary
to treat the
> dictionary as a true, first-class object as well. You can create
new words
> only by using CREATE or : (VARIABLE and CONSTANT are defined in
terms of
> CREATE), both of which work by taking their input NOT from the
program, but
> from the current input stream. Also note that there is no way to
iterate

Dictionary in Forth cannot be a first class object. If you made it a
first class object you'd end up with a new language. In Forth there
are no first class objects and nothing is off-limits and like you
said that's what ANS Forth is trying to change - to force
programmers to treat certain things as special such as the return
stack.

The way I see CREATE and : and other defining words is as a part of
a separate language. That separate language also includes all
immediate and compiling words.
Chuck Moore went as far as to eliminate defining words and replace
them with colors in ColorForth to further separate defining words
from normal words.

Ivan

Samuel Falvo — 2003-03-15 21:13:50

> C isn't the most portable of languages by far. The reason it's so

I used C because it's considered to be a "portable" language by academia and
industry alike.

> popular has nothing to do with it's portability - the portability is

Historical evidence seems to suggest otherwise. As I currently understand the
history of C and Unix, the reason it's so popular is precisely because the
compiler was touted, very early on, as being portable by AT&T. "Look, we can
implement Unix on both the PDP-7 AND the PDP-11!" It spread throughout the
academic community like wildfire, and a whole generation of programmers grew up
on it. Another claim to fame for C was its low-level interface to systems
hardware (e.g., explicit pointer operations, etc). Once Unix was ported to the
VAX architecture, C's fate as a foundation language was sealed. This is the
reason it's popular today.

> the result of standardization. These days most languages with well

The popularity of C was readily apparent long before ANSI or ISO got involved.

> defined and recognized standards are at least as portable as C.
> Being a relatively low-level language, C isn't really all that
> portable by nature just like Forth. Portability in C and Forth is
> mostly enforced through standards.

I disagree.

Portability in C is through the specification of the language itself, a mapping
between syntactical elements and their run-time behavior, in addition to the
supporting, standardized run-time libraries. Many people don't realize that
the standard C libraries are actually a proper subset of Unix, 3rd edition.
Even today, the standard run-time environment for C is implemented, if not in
the Unix kernel itself, then at least in the library that interfaces user-land
processes to the kernel: libc. This, BTW, further cements the tight coupling a
language has with its operating system, a trait many people insist that C
lacks.

Portability in Forth is derived through other means, especially since Forth has
no common syntax, except for the colon definition (and ColorForth even does
away with that). However, unlike C, it does have something much more powerful:
a common model of the run-time *environment*, whether virtualized or physically
implemented. By blurring the distinction between basic primitive and user-made
construction, portability can be achieved through merely (re)defining basic
primitives. It fulfills Backus' desired requirement that larger programs be
composed from smaller programs, and not through a sequence of statements each
of which is partially responsible for partial state changes. Though Forth
isn't a functional language (at least, not in a pure sense), it meets a *LOT*
of FP's requirements.

> Portability does not have to be a myth though. It could be maximised
> by removing all system specific types from language (i.e. ints,
> floats, etc.) and replacing them with something more generic. Of

This is precisely what factoring your code provides, hence
portability-through-factoring still applies.

> course such a language would probably be fairly inefficient compared
> to C even with optimizing compilers but I wouldn't think the
> performance would be much worse then that of most other functional
> languages (which is still pretty bad though).

Too many people are obscessed(sp?) with speed. Just write the damn program,
and get it working. Worry about speed later; that's why profilers exist. :-)
Lisp is a horrifically slow system in most implementations; but yet, it's fast
enough to power virtually ALL of Yahoo!'s web serving needs, including e-mail,
it's shopping mall, and its online gaming community. Python is often times
slower than Lisp, and demonstrably slower than Perl, but yet it powers one of
the world's most sophisticated and growing web applications environment: Zope.

In martial arts, there's a cliche that evolved, because it's provably true:
Speed Is An Illusion.

The more I read the literature, the more I'm absolutely convinced that the
sorry state of software today ultimately derives from the programmer's total
lack of understanding of how computers *really* work under the hood, and
therefore, what does or does not influence performance the greatest. In their
quest to make everything as fast as possible, they've created computer designs
with more transistors in one chip than the whole population of a large city
(sometimes even a small country), software as inlined and unrolled as possible,
attempting to minimize branches. But what they fail to grasp is that, in order
to feed those transistors data, you need caches, and to feed those caches, you
need still more caches, ultimately terminating in our relatively slow DRAMs
today. And ultimately, all that unrolled code has to be transferred over the
"von Neumann Bottleneck" (as Backus calls it, and which I agree).

Factoring your code optimizes for size, for sure, but it doesn't necessarily
have to come at the cost of speed. I've found that smaller code can sometimes
run as fast, if not faster than, larger code! The only explanation I can think
of is that everything sits inside the CPU's inner-most caches. Subroutine call
overheads act to slow things down, but when thrashing the caches, the
advantages of unrolling and inlining become equally moot (especially when the
bus is in regular contention with I/O device activity).

There was interesting research performed by Anton Ertl that, for certain
classes of processors, a direct-threaded Forth implementation actually ran
*faster* than an equivalent, traditionally-written assembly language program.
The reason was that, despite having higher clock counts because of the extra
interpretation level, the lack of cache thrashing more than made up for the
difference.

Obviously, the decision of when to unroll and when to call is going to be not
only processor dependent, but bus/motherboard dependent as well (this includes,
by extension, what peripherals are attached to the bus). The number of
variables involved with making a sound decision is simply too much for people
to grasp up front, and is utterly impossible for a compiler to predict. Hence,
you should always design your code as tightly and as well factored as possible,
if for nothing else than the *speculation* that the small size *can* give it
reasonable performance (it runs entirely from cache). Use unconditional
branches as much as possible, so as to minimize the chances of being
mispredicted. Then, if you *really* need the speed, you can hand-optimize
where necessary, using profilers to locate the spots that needs it most. This
forms an interactive feedback loop, resulting in an iterative, step-wise
refinement of your program to suit a particular set of requirements.

But this brings up another good point: if you minimize the subroutine overhead
to as little as possible (ideally being 0 clocks, but 2 clocks being more
realistic), suddenly, well-factored software becomes highly desirable. Smaller
code footprint takes less time to transfer over the bus, takes less cache
space, and would execute with effectively zero overhead. This suggests that
complex, transistor-intensive systems that attempt to minimize the amount of
branching is the WRONG WAY TO GO for processor designs. The 680x0 and x86, and
a whole slew of RISC architectures, seem to promulgate this design philosophy,
much to the detriment of the state of the art.

Consider that a 4MHz 6502 (an 8-bit, highly memory bound processor, with an
8-bit memory path) was able to out-perform an 5MHz 8086 (a 16-bit,
register-to-register processor, with a 16-bit memory path) universally across a
number of benchmarks put at it. There are two reasons for this: one, I've
already covered -- memory latency will severely decrease performance. The
other, however, is that the 6502's subroutine overhead is only 12 cycles (6 for
a call, 6 for a return), versus the 8086's total of 39!!

Subroutine call overheads for modern processors have only gotten worse. While
the Pentium can resolve and execute a subroutine call much faster than the 39
cycles of yesteryear, there are other factors that need to be considered, such
as the time taken reloading a cache-line, mis-predicted branches that flush not
only the instruction queue, but also the pipeline, etc.

Note that stack architectures need not be the only architectures that are
optimized for branching, though they certainly do require the least amount of
transistors. The only reason the 6502 takes 6 cycles to call or return from a
subroutine is because it only has one ALU and one datapath leading to it
internally. With multiple datapaths, it could be reduced to 3 cycles --
closely approximating the performance of an ideal stack processor. With a
wider bus to memory, and instruction pre-fetching, it can be reduced to 1.
With caching the stack using a special purpose cache, it can be reduced to 0.5.
All this IS possible on a register or accumulator architecture. Just because
Intel hasn't done it, doesn't mean it can't possibly be done.

Speaking of stack processors, interestingly enough, the LACK of
superscalability of stack architectures would seem to make them highly
desirable for implementation of graph reduction machines. Single-cycle
instruction execution performance, combined with conditional skips (instead of
conditional branches) or predication in a stack architecture would require so
few transistors (on the order of 2000 to 4000 gates at best) that you can
literally pack *HUNDREDS* of these processors on a single processor die using
modern fabrication techniques, and still have enough room for a multi-ported,
512KB level-1 cache. Chuck Moore's 25X architecture, though not fabricated yet
(and may never be, unfortunately), has been successfully simulated with rather
intriguing results (it is the only architecture I'm aware of that can emulate
most FPGA functions in software). Functional programming entheusiasts would go
bonkers over such an architecture, since it's basically ready made for
applicative functional programming languages.

> Not really a better solution - that's what #ifdefs mostly do: you
> end up with a bunch of different versions of DoBlort() for different
> processors. It does improve readability a bit though.

Without drawing on the literature that is commonly available in computer
science and engineering circles, I must sorely and vehemently disagree with
your assertion that preprocessor macros make for higher readability source,
based solely on my experience maintaining legacy code for many years.

The widespread success of (even systems) programming languages that do not have
preprocessor macros stands as strong evidence of their redundancy. Why should
I ever have to read something like #ifdef when I just want to know what
functionality a module needs? Configuration management should not exist inside
the program source -- it should exist, at best, in its Makefile (that's what
it's there for, after all).

> IIRC Chuck Moore advocates rewriting a program for every new system
> instead of porting an existing program.

Chuck Moore, when working for Forth, Inc., would port a complete Forth
environment over to a whole new processor architecture in a matter of hours to
days. He achieved this speed by writing a new assembler for the target
architecture (pretty much an absolute necessity, no matter how you look at it),
and then wrote a new low-level set of words which the higher-level words relied
upon. This set of low-level words is known and well-bounded. This was
portability-by-factoring at its finest.

Even today, his ColorForth environment is architected this way, having its
foundations on MachineForth. MachineForth is a set of 29 primitives,
implemented as macros/immediate words. EVERYTHING else in the Forth derives
from these primitives, and are written in Forth itself.

He rewrote his OKAD software because the previous architecture didn't out well
for his future design work. Moore admits that the original OK and OKAD systems
were failed experiments. It was too tightly integrated, didn't allow for
programmability, etc. Adding these kinds of things can definitely cause global
changes in software, regardless of how well factored it is. When the basic,
underlying metaphor of a project changes, you almost always have to start over
from scratch. This is as true in C as it is in Forth. (e.g., changing the
user interface metaphor from VI to Raskin's THE in my block editor would force
me to do a grass-roots redesign of the application.)

OKAD-II has been ported between the F21 and Pentium systems. It's remained
largely the same code base.

Chuck doesn't advocate blind re-writes of whole applications. If you read his
material, he advocates instead that you consider the *possibility* of a
rewrite. The distinction is an important one.

> Dictionary in Forth cannot be a first class object. If you made it a
> first class object you'd end up with a new language. In Forth there
> are no first class objects and nothing is off-limits and like you

This is a contradiction: if nothing is off-limits, how can the data-stack not
be a first-class object? Return stack? Dictionary?

You say that the dictionary "cannot" be a first-class object in Forth, as if
you invented the language yourself. Prove to me why it can't be a first-class
object in Forth.

> The way I see CREATE and : and other defining words is as a part of
> a separate language. That separate language also includes all
> immediate and compiling words.
> Chuck Moore went as far as to eliminate defining words and replace
> them with colors in ColorForth to further separate defining words
> from normal words.

Instead of defining words, he has defining colors. The basic, underlying
mechanisms are still *exactly* the same: when the interpretter sees a red word,
it creates a word header. When it sees a green word, it compiles a subroutine
call. When it sees a green number, it compiles a load instruction. All he's
managed to do is remove the modality of Forth. Otherwise, it is no different
from normal Forth, including its lack of ability to dynamically create words at
run-time.


__________________________________________________
Do you Yahoo!?
Yahoo! Web Hosting - establish your business online
http://webhosting.yahoo.com

John Cowan — 2003-03-15 23:15:19

Samuel Falvo scripsit:

> Lisp is a horrifically slow system in most implementations;

This is absolutely false, a meme that needs to be stomped out wherever
it appears. As long ago as 1973, the Lisp compiler on the DEC PDP-10
was outspeeding the vendor's Fortran compiler. Modern Lisp compilers
do at least as well.

> In martial arts, there's a cliche that evolved, because it's provably true:
> Speed Is An Illusion.

"A Lisp programmer is one who knows the value of everything, but the cost
of nothing."
--Alan J. Perlis (http://www.cs.yale.edu/homes/perlis-alan/quotes.html)

--
John Cowan http://www.ccil.org/~cowan cowan@...
To say that Bilbo's breath was taken away is no description at all. There
are no words left to express his staggerment, since Men changed the language
that they learned of elves in the days when all the world was wonderful.
--_The Hobbit_

Ivan Tomac — 2003-03-16 03:49:28

--- In concatenative@yahoogroups.com, Samuel Falvo <falvosa@y...>
wrote:
> > C isn't the most portable of languages by far. The reason it's
so
>
> I used C because it's considered to be a "portable" language by
academia and
> industry alike.
>
> > popular has nothing to do with it's portability - the
portability is
>
> Historical evidence seems to suggest otherwise. As I currently
understand the
> history of C and Unix, the reason it's so popular is precisely
because the
> compiler was touted, very early on, as being portable by
AT&T. "Look, we can
> implement Unix on both the PDP-7 AND the PDP-11!" It spread
throughout the
> academic community like wildfire, and a whole generation of
programmers grew up
> on it. Another claim to fame for C was its low-level interface to
systems
> hardware (e.g., explicit pointer operations, etc). Once Unix was
ported to the
> VAX architecture, C's fate as a foundation language was sealed.
This is the
> reason it's popular today.
>

AT&T did publicize C a fair bit. At the time C offered a unique mix
of speed while it
was still reasonably high-level compared to alternatives and was
also easier to grasp
for those who already knew a language such as Pascal.
Another thing that I think had a big effect on C's popularity (and
this is just my
opinion) is that it was made in a big company rather than in a
garage or a uni.
Many people back then and even now think that if something is made
in a company it has
to be better than something made in a garage or a uni.

> > the result of standardization. These days most languages with
well
>
> The popularity of C was readily apparent long before ANSI or ISO
got involved.
>
> > defined and recognized standards are at least as portable as C.
> > Being a relatively low-level language, C isn't really all that
> > portable by nature just like Forth. Portability in C and Forth
is
> > mostly enforced through standards.
>
> I disagree.
>
> Portability in C is through the specification of the language
itself, a mapping
> between syntactical elements and their run-time behavior, in
addition to the
> supporting, standardized run-time libraries. Many people don't
realize that

C is not portable by nature. Any language that allows you to mess
around with pointers,
that defines ints and floats and other types in a certain way, that
allows you to mess
around with alignment of data on purpose is not portable by nature.
It's a fact that you can write a portable program in C if you try
really hard but it's
also a fact that you can easily write a piece of code in C that
won't be portable at all.
In a way this is similar to how you can write object oriented
programs in plain C even
though C is not an object oriented language by nature.

> the standard C libraries are actually a proper subset of Unix, 3rd
edition.
> Even today, the standard run-time environment for C is
implemented, if not in
> the Unix kernel itself, then at least in the library that
interfaces user-land
> processes to the kernel: libc. This, BTW, further cements the
tight coupling a
> language has with its operating system, a trait many people insist
that C
> lacks.
>
> Portability in Forth is derived through other means, especially
since Forth has
> no common syntax, except for the colon definition (and ColorForth
even does

ANS-Forth is about as portable as C. Of course writing portable
programs in Forth prevents
you from using the things that make Forth great IMO (and from your
comments I think you
agree with that statement).

> *snip*
>
> > Portability does not have to be a myth though. It could be
maximised
> > by removing all system specific types from language (i.e. ints,
> > floats, etc.) and replacing them with something more generic. Of
>
> This is precisely what factoring your code provides, hence
> portability-through-factoring still applies.
>

No, this is different. For example a programming language that
doesn't have 32-bit
ints and floats but instead just has bignums eliminates a whole
bunch of portability
problems that factoring cannot.

> > course such a language would probably be fairly inefficient
compared
> > to C even with optimizing compilers but I wouldn't think the
> > performance would be much worse then that of most other
functional
> > languages (which is still pretty bad though).
>
> Too many people are obscessed(sp?) with speed. Just write the
damn program,
> and get it working. Worry about speed later; that's why profilers
exist. :-)
> Lisp is a horrifically slow system in most implementations; but
yet, it's fast
> enough to power virtually ALL of Yahoo!'s web serving needs,
including e-mail,
> it's shopping mall, and its online gaming community. Python is
often times
> slower than Lisp, and demonstrably slower than Perl, but yet it
powers one of
> the world's most sophisticated and growing web applications
environment: Zope.
>

It depends on the application. There are too many applications out
there for
which functional languages would simply be too slow considering the
current
state of compilers.

> *snip*
>
> > Not really a better solution - that's what #ifdefs mostly do:
you
> > end up with a bunch of different versions of DoBlort() for
different
> > processors. It does improve readability a bit though.
>
> Without drawing on the literature that is commonly available in
computer
> science and engineering circles, I must sorely and vehemently
disagree with
> your assertion that preprocessor macros make for higher
readability source,
> based solely on my experience maintaining legacy code for many
years.
>

I think you misunderstood what I said. Actually I should have
phrased this a bit
better. I was agreeing with you that splitting things up into
modules is more
readable than conditional defines. But as far as portability goes
it's pretty
much the same, you're not making the code more portable, you're only
making it
more readable.

> The widespread success of (even systems) programming languages
that do not have
> preprocessor macros stands as strong evidence of their
redundancy. Why should
> I ever have to read something like #ifdef when I just want to know
what
> functionality a module needs? Configuration management should not
exist inside
> the program source -- it should exist, at best, in its Makefile
(that's what
> it's there for, after all).
>

Conditional defines and preprocessors aren't bad. They often
complement the
language. They can often improve readability of the code and
decrease redundancy
in the code if they're used carefully. Defining and immediate words
in Forth
act in a way that resembles preprocessor macros.

> *snip*
> Chuck doesn't advocate blind re-writes of whole applications. If
you read his
> material, he advocates instead that you consider the *possibility*
of a
> rewrite. The distinction is an important one.

I stand corrected.

>
> > Dictionary in Forth cannot be a first class object. If you made
it a
> > first class object you'd end up with a new language. In Forth
there
> > are no first class objects and nothing is off-limits and like
you
>
> This is a contradiction: if nothing is off-limits, how can the
data-stack not
> be a first-class object? Return stack? Dictionary?
>
> You say that the dictionary "cannot" be a first-class object in
Forth, as if
> you invented the language yourself. Prove to me why it can't be a
first-class
> object in Forth.
>

Not a contradiction. The preferred way of programming in Forth is
modifying the
language itself to suit the task at hand. By doing that you yourself
are
changing Forth. This is something ANS-Forth is against.

The fact that defining words take their input from the input stream
and not the
program itself further reinforces the comparison with preprocessor
macros.

If you made the dictionary a first class object in Forth you'd end
up with a new
language - it wouldn't be Forth anymore. Forth allows you to do this
and if you
really need to do this for the task at hand even encourages you to
do this but
the end result is not Forth.

> > The way I see CREATE and : and other defining words is as a part
of
> > a separate language. That separate language also includes all
> > immediate and compiling words.
> > Chuck Moore went as far as to eliminate defining words and
replace
> > them with colors in ColorForth to further separate defining
words
> > from normal words.
>
> Instead of defining words, he has defining colors. The basic,
underlying
> mechanisms are still *exactly* the same: when the interpretter
sees a red word,
> it creates a word header. When it sees a green word, it compiles
a subroutine
> call. When it sees a green number, it compiles a load
instruction. All he's
> managed to do is remove the modality of Forth. Otherwise, it is
no different
> from normal Forth, including its lack of ability to dynamically
create words at
> run-time.
>

Yes, defining colors. The mechanisms are the same. But it's because
they're special
and not like normal words. It's because they belong to a separate
language that
coexists in symbiosis with Forth itself.

Ivan

Ivan Tomac — 2003-03-16 03:52:13

I apologise about the formatting, I'm having some problems with my
web browser.

Ivan

Samuel Falvo — 2003-03-16 19:56:49

> Many people back then and even now think that if something is made
> in a company it has
> to be better than something made in a garage or a uni.

Java.

> If you made the dictionary a first class object in Forth you'd end
> up with a new
> language - it wouldn't be Forth anymore. Forth allows you to do this
> and if you
> really need to do this for the task at hand even encourages you to
> do this but
> the end result is not Forth.

I disagree with this. If this were true, then as soon as I write my first
:-definition, the language I'm programming in is no longer Forth. By
extension, absolutely no existing ANSI Forth implementations are Forth, since
they all define words (usable by applications even) above and beyond the ANSI
standard.

If CREATE and : were defined in terms of a more orthogonal interface to the
dictionary, then it'd still be Forth. Just because I expose the words that
create a new word header given any arbitrary string on the stack (and such
words do exist in all Forths; though, unfortunately, 99% of them are
headerless!) doesn't cause it to not be Forth anymore. This is like saying
Visual C++ and Borland C++ aren't C++ anymore, because they expose the Windows
API to the programmer.

> Yes, defining colors. The mechanisms are the same. But it's because
> they're special
> and not like normal words. It's because they belong to a separate
> language that
> coexists in symbiosis with Forth itself.

I apologize, but I just can't agree with this. The issue with the colors is
one of modality, not language structure, because colors do not exist as
separate tokens, but rather as an attribute of each word found in the source.
(Though, it's intriguing to note that, as modeless as ColorForth is, it's still
modal. : VARIABLE and other such words set the behavior upon seeing a red
word, and the programmer must be aware of this at all times. But I digress.)

--
Samuel A. Falvo II

__________________________________________________
Do you Yahoo!?
Yahoo! Web Hosting - establish your business online
http://webhosting.yahoo.com

phimvt@lurac.latrobe.edu.au — 2003-03-17 06:35:42

On Fri, 14 Mar 2003, John Cowan wrote:

> phimvt@... scripsit:
>
> > Indeed, the use of local procedures as parameters in recursive
> > calls appears to be a sort of black art, and I seem to be one
> > of its few practitioners.
>
> In Pascal, I suppose you mean.

Yes, I did. I should have made that clearer.

> Of course the entire Lisp (and especially
> Scheme) community practices this art with abandon, and doesn't consider
> it black.

Do you happen to know of a reference? It might be good for snarfing
for Joy.

> In Scheme, furthermore, you can return a local procedure as
> the value of one of its enclosing procedures.
I think this will also be true of ML and Haskell - all those languages
in which functions are "first class citizens" (and live on the heap).
I have practised the art in Joy in the two backtracking libraries.

>
> The GNU dialect of C allows local procedures and procedure arguments,

Do you happen to know whether it really passes on (as a static link or
as a display) the current local environment? Without doing that it
would not allow the black art. [I seem to remember reading somewhere
that a language which does not rely on a heap has to decide between
1) passing on the current local environment, or 2) allowing assignments
(of global procedures only) to procedural variables. But I cannot
remember where I saw this. It made sense at the time.]

> but ANSI C does not.

- Manfred

John Cowan — 2003-03-17 12:44:32

phimvt@... scripsit:

> Do you happen to know of a reference? It might be good for snarfing
> for Joy.

Not offhand. I think the Scheme community considers the technique obvious,
and as such it is only documented as folklore.

> I think this will also be true of ML and Haskell - all those languages
> in which functions are "first class citizens" (and live on the heap).
> I have practised the art in Joy in the two backtracking libraries.

Unless I am very mistaken, this is *not* true of Standard ML, which does
not implement full continuations.

> > The GNU dialect of C allows local procedures and procedure arguments,
>
> Do you happen to know whether it really passes on (as a static link or
> as a display) the current local environment?

It really does. In order to make it transparent to the caller whether
it is invoking an ordinary (global) procedure or a nested one, the
function pointer for a nested procedure points to a short stretch of
code called a "trampoline", which sets up the callee's environment correctly
and then jumps to it. "The trampoline you can understand is not the
true trampoline." See
http://people.debian.org/~aaronl/Usenix88-lexic.pdf

Attempting to invoke a nested procedure whose containing procedure has
exited causes undefined behavior, of course.


See http://gcc.gnu.org/onlinedocs/gcc-3.2.2/gcc/Nested-Functions.html#Nested%20Functions
Now that gcc is so pervasive, its extensions to C (most of which were
introduced in order to debug features put into the back end to implement
Pascal, Algol 60, and other languages) are well worth considering, notably
the labels-as-values that makes true assigned GOTO possible -- great for
implementing bytecode interpreters!
An index to the features is at
http://gcc.gnu.org/onlinedocs/gcc-3.2.2/gcc/C-Extensions.html#C%20Extensions

--
John Cowan http://www.ccil.org/~cowan cowan@...
To say that Bilbo's breath was taken away is no description at all. There
are no words left to express his staggerment, since Men changed the language
that they learned of elves in the days when all the world was wonderful.
--_The Hobbit_

phimvt@lurac.latrobe.edu.au — 2003-03-18 07:06:58

On Mon, 17 Mar 2003, John Cowan wrote:

> phimvt@... scripsit:

[.. a propos implementing backtracking by means of continuations ..]

> > Do you happen to know of a reference? It might be good for snarfing
> > for Joy.
>
> Not offhand. I think the Scheme community considers the technique obvious,
> and as such it is only documented as folklore.

I can only find the well-known call-cc technique (+ the amb function).
But I meant something different: using recursive calls with a local
procedure as a parameter to the recursive call. See the example at the
end.

> > I think this will also be true of ML and Haskell - all those languages
> > in which functions are "first class citizens" (and live on the heap).
> > I have practised the art in Joy in the two backtracking libraries.
>
> Unless I am very mistaken, this is *not* true of Standard ML, which does
> not implement full continuations.

Since "the art" does not involve any special features such as call-cc,
something like the example at the end should also work in Scheme, Common
Lisp, ML and Haskell - I think.

> > > The GNU dialect of C allows local procedures and procedure arguments,
> >
> > Do you happen to know whether it really passes on (as a static link or
> > as a display) the current local environment?
>
> It really does. In order to make it transparent to the caller whether
> it is invoking an ordinary (global) procedure or a nested one, the
> function pointer for a nested procedure points to a short stretch of
> code called a "trampoline", which sets up the callee's environment correctly
> and then jumps to it. "The trampoline you can understand is not the
> true trampoline." See
> http://people.debian.org/~aaronl/Usenix88-lexic.pdf

Thank you for this, it was an eye-opener for me. This seems to be a way
of passing on the static link in languages in which such a link is
not normally passed on in procedure calls. Very clever. And incidentally,
perhaps the best way of understanding static links even when local
procedures can *not* be used as parameters is in Wirth's original book,
Algorithms + Data Structes = Programs, last Chapter (which unfortunately
got omitted in later editions).

> See http://gcc.gnu.org/onlinedocs/gcc-3.2.2/gcc/Nested-Functions.html#Nested%20Functions
> Now that gcc is so pervasive, its extensions to C (most of which were
> introduced in order to debug features put into the back end to implement
> Pascal, Algol 60, and other languages) are well worth considering, notably
> the labels-as-values that makes true assigned GOTO possible -- great for
> implementing bytecode interpreters!

I have often wondered whether Joy could be implemented in a way
which is very different from the current one. Anyone having thoughts
on this?

I include one program which uses a local procedure as a parameter
in a recursive call to the enclosing procedure. Note that there
is only one local variable n : integer.

The program reads
lines of numbers, and for each line that it has read it will write one
line, containing the even numbers in their original order, then a
space of four characters, then the odd numbers in their original
order.

PROGRAM oddevn(input,output);

PROCEDURE space;
BEGIN write(' ') END;

PROCEDURE odev(PROCEDURE cp);
VAR n : integer;

PROCEDURE writelater;
BEGIN cp; write(n:0,' ') END;

BEGIN (* odev *)
IF eoln THEN cp ELSE
BEGIN
read(n);
IF n MOD 2 = 0
THEN BEGIN write(n:0,' '); odev(cp) END
ELSE odev(writelater) (* recursive call with local proc *)
END
END; (* odev *)

BEGIN (* main, oddevn *)
WHILE NOT eof DO
BEGIN odev(space); readln; writeln END
END. (* main, oddevn *)

Of course this is a silly program, used to illustrate second order
recursion, from Chapter 8 of my Pascal page.
http://www.latrobe.edu.au/philosophy/phimvt/s00bok.html
The technique is used seriously in Chapters 9 10 11 15 16 20 to
implementing backtracking. There is also some third order recursion,
and, if you can stomach this sort of thing, fourth order recursion
(in the Appendix, part 4).

It would be of interest to me to know whether gcc can handle
this sort of thing (as I mentioned before, the version of p2c
that I once tried failed.) Also, I am confident (without positive
proof) that this sort of thing works equally well in Scheme, Lisp,
ML, Haskell (and, if I understand it correctly, perhaps in
stackless python). Does anybody want to try ?

Thanks for the comments, John.

- Manfred

Samuel Falvo — 2003-03-18 20:03:01

> PROGRAM oddevn(input,output);
>
> PROCEDURE space;
> BEGIN write(' ') END;
>
> PROCEDURE odev(PROCEDURE cp);
> VAR n : integer;
>
> PROCEDURE writelater;
> BEGIN cp; write(n:0,' ') END;
>
> BEGIN (* odev *)
> IF eoln THEN cp ELSE
> BEGIN
> read(n);
> IF n MOD 2 = 0
> THEN BEGIN write(n:0,' '); odev(cp) END
> ELSE odev(writelater) (* recursive call with local proc *)
> END
> END; (* odev *)
>
> BEGIN (* main, oddevn *)
> WHILE NOT eof DO
> BEGIN odev(space); readln; writeln END
> END. (* main, oddevn *)

Thanks for posting this example. I'll try re-coding it in Oberon, to see if
Oberon behaves as expected. I'll be using the oo2c compiler. Perhaps Steve
can use the Linux Native Oberon environment to see if his compiler works
similarly?

I know this is wildly off-topic for the list, but I think this is a worthwhile
investigation, as studying how these languages support the feature can provide
valuable clues/insights on how to do similar things with concatenative
languages.

--
Samuel A. Falvo II


__________________________________________________
Do you Yahoo!?
Yahoo! Platinum - Watch CBS' NCAA March Madness, live on your desktop!
http://platinum.yahoo.com

Samuel Falvo — 2003-03-18 20:59:13

OK, here's the Oberon program I've written:

MODULE OddEven;
IMPORT In, Out;

TYPE
Callback = PROCEDURE();

VAR
ignoredLine: ARRAY 128 OF CHAR;

PROCEDURE space();
BEGIN
Out.String( " " )
END space;

PROCEDURE odev( cb: Callback );
VAR
n: INTEGER;

PROCEDURE Print( n: INTEGER );
BEGIN
Out.Int( n, 0 );
Out.String( " " );
END Print;

PROCEDURE WriteLater();
BEGIN
cb();
Print( n );
END WriteLater;

BEGIN
In.Int(n);
IF (n MOD 2) = 0 THEN
Print( n );
odev( cb )
ELSE
odev( WriteLater )
END
END odev;

BEGIN
WHILE ~In.Done() DO
odev( space );
In.Line( ignoredLine );
Out.Ln()
END
END OddEven.

While the syntax of the compiler fully supports the notation of recursively
invoking inner functions, and passing them around as procedure pointers, the
oo2c compiler does not permit this. However, if oo2c were to implement
trampolines like GCC does for references to inner procedures, then there's no
reason why Oberon shouldn't support the feature. However, issues of
type-safety come into play when doing this, which is presumably why Oberon
doesn't permit this construction.

Shucks. Oh well...

--
Samuel A. Falvo II


__________________________________________________
Do you Yahoo!?
Yahoo! Platinum - Watch CBS' NCAA March Madness, live on your desktop!
http://platinum.yahoo.com

Ivan Tomac — 2003-03-18 22:37:09

--- In concatenative@yahoogroups.com, Samuel Falvo <falvosa@y...>
wrote:
> > If you made the dictionary a first class object in Forth you'd
end
> > up with a new
> > language - it wouldn't be Forth anymore. Forth allows you to do
this
> > and if you
> > really need to do this for the task at hand even encourages you
to
> > do this but
> > the end result is not Forth.
>
> I disagree with this. If this were true, then as soon as I write
my first
> :-definition, the language I'm programming in is no longer Forth.
By
> extension, absolutely no existing ANSI Forth implementations are
Forth, since
> they all define words (usable by applications even) above and
beyond the ANSI
> standard.
>
> If CREATE and : were defined in terms of a more orthogonal
interface to the
> dictionary, then it'd still be Forth. Just because I expose the
words that
> create a new word header given any arbitrary string on the stack
(and such
> words do exist in all Forths; though, unfortunately, 99% of them
are
> headerless!) doesn't cause it to not be Forth anymore. This is
like saying
> Visual C++ and Borland C++ aren't C++ anymore, because they expose
the Windows
> API to the programmer.
>

That has nothing to do with this. Defining new words in Forth is just
like writing a library of functions in C. Neither breaks the
language.
If however a newly defined word replaces, breaks or modifies the
behaviour of existing words as defined by a standard then you do end
up with a new language or at least a new dialect - even more so
since Forth itself doesn't have any reserved words and allows you to
modify and change anything you want. Making Forth dictionary a first
class object would change the behaviour of many Forth words and
hence change Forth itself unless you created a new set of words to
manipulate that dictionary and unless that dictionary was separated
from the standard dictionary.

> > Yes, defining colors. The mechanisms are the same. But it's
because
> > they're special
> > and not like normal words. It's because they belong to a
separate
> > language that
> > coexists in symbiosis with Forth itself.
>
> I apologize, but I just can't agree with this. The issue with the
colors is
> one of modality, not language structure, because colors do not
exist as
> separate tokens, but rather as an attribute of each word found in
the source.
> (Though, it's intriguing to note that, as modeless as ColorForth
is, it's still
> modal. : VARIABLE and other such words set the behavior upon
seeing a red
> word, and the programmer must be aware of this at all times. But
I digress.)
>

I don't think you understood what I was trying to say.
Forth itself is typeless, has no reserved words and gives the
programer amazing amount of freedom. The reason why he replaced the
defining words with colors is so that he could conceptually make
them stand out from normal words. Again I should point out the
similarities between defining and immediate words and preprocessor
macros.

Ivan

Samuel Falvo — 2003-03-18 23:29:17

> That has nothing to do with this. Defining new words in Forth is just
> like writing a library of functions in C. Neither breaks the
> language.

It has everything to do with it; you suggested that my proposal to add words
which expose the dictionary in a more run-time accessible way would change the
language significantly to no longer be considered Forth. This is, of course,
false, as CREATE and : are themselves already built up upon such functionality.
My concern is that such functionality isn't made public (much less portable)
to the programmer, which severely hinders certain types of metaprogramming
under Forth.

> If however a newly defined word replaces, breaks or modifies the
> behaviour of existing words as defined by a standard then you do end
> up with a new language or at least a new dialect - even more so
> since Forth itself doesn't have any reserved words and allows you to
> modify and change anything you want. Making Forth dictionary a first
> class object would change the behaviour of many Forth words and
> hence change Forth itself unless you created a new set of words to
> manipulate that dictionary and unless that dictionary was separated
> from the standard dictionary.

I asked you once before for a proof of this, and I'm going to ask you again.
Until I see a definite proof, I cannot and will not accept this assertion.

> them stand out from normal words. Again I should point out the
> similarities between defining and immediate words and preprocessor
> macros.

You say this as if it were somehow ignored by me. As the author of several
Forth implementations, and one more coming shortly, I can assure you that I
know full well the differences and similarities between preprocessor macros and
immediate words. However, it is not at all relavent to the issue.

--
Samuel A. Falvo II


__________________________________________________
Do you Yahoo!?
Yahoo! Platinum - Watch CBS' NCAA March Madness, live on your desktop!
http://platinum.yahoo.com

Steven Shaw — 2003-03-19 06:03:58

> I can only find the well-known call-cc technique (+ the amb function).

What is the amb function?

> But I meant something different: using recursive calls with a local
> procedure as a parameter to the recursive call. See the example at the
> end.

Pretty sure that schemers would just cons up a list to remember the odd
numbers rather than using the runtime stack. However, it would be possible
to port it to Scheme fairly intact. I gave it a try:

;-------------------------------------------------
(define (display-it n) (display n) (display " "))

; similar to Pascal version of oddeven without the console input
(define (odd-even list)
(define (space) (display " "))
(define (odd-dev list cp)
(define n "?")
(define (write-later) (cp) (display-it n))
(if (null? list)
(cp)
(begin
(set! n (car list))
(if (even? n)
(begin (display-it n) (odd-dev (cdr list) cp))
(odd-dev (cdr list) write-later)))))
(odd-dev list space))

; this version doesn't use recursive procedure parameters, it
; just constructs a list of the odds
(define (odd-even-2 l)
(define (odd-dev l odds)
(if (null? l)
(begin
(display " ")
(for-each display-it (reverse odds)))
(if (even? (car l))
(begin (display-it (car l)) (odd-dev (cdr l) odds))
(odd-dev (cdr l) (cons (car l) odds)))))
(odd-dev l '()))

(odd-even '(1 2 3 4 5 6 7 8 9))
(display "\n")
(odd-even-2 '(1 2 3 4 5 6 7 8 9))
;-------------------------------------------------

I'm only new to Scheme so my code probably isn't as good as it could be.

Steve.

Ivan Tomac — 2003-03-19 08:33:43

--- In concatenative@yahoogroups.com, Samuel Falvo <falvosa@y...>
wrote:
> > That has nothing to do with this. Defining new words in Forth is
just
> > like writing a library of functions in C. Neither breaks the
> > language.
>
> It has everything to do with it; you suggested that my proposal to
add words
> which expose the dictionary in a more run-time accessible way would
change the
> language significantly to no longer be considered Forth. This is,
of course,
> false, as CREATE and : are themselves already built up upon such
functionality.
> My concern is that such functionality isn't made public (much less
portable)
> to the programmer, which severely hinders certain types of
metaprogramming
> under Forth.
>
> > If however a newly defined word replaces, breaks or modifies the
> > behaviour of existing words as defined by a standard then you do
end
> > up with a new language or at least a new dialect - even more so
> > since Forth itself doesn't have any reserved words and allows you
to
> > modify and change anything you want. Making Forth dictionary a
first
> > class object would change the behaviour of many Forth words and
> > hence change Forth itself unless you created a new set of words
to
> > manipulate that dictionary and unless that dictionary was
separated
> > from the standard dictionary.
>
> I asked you once before for a proof of this, and I'm going to ask
you again.
> Until I see a definite proof, I cannot and will not accept this
assertion.
>

I thought it would have been pretty obvious. Let's start with an easy
example. Take a language like C, replace reserved words like if,
else, switch, etc. with something like foo, moo, blurp, etc. (or even
better switch them around so that if is else, else is switch, switch
is for and for is if) The language you end up with won't be C
anymore. It's structure will be identical to C and programming in it
will be identical to C as well but it won't be able to compile C
programs anymore. It could be described as a dialect of C but it's
definitley not C anymore.
Same thing happens with Forth, if you add words that modify or break
the behaviour of existing words then that Forth interpreter/compiler
won't be able to properly interpret other Forth programs. It's as
simple as that.
The more things you change/break, the less will the system behave
like a standard Forth system. Making the dictionary a first class
object and adding the ability to create words at run-time is a pretty
big change - enough to break a whole bunch of programs previously
written for that Forth system. Not to mention that programming in
such a system would be completely different than normal Forth since
you could create words that create other words at run-time and do all
sorts of things that you just can't do with Forth. Sure it would be
some dialect of Forth just like Objective C is still a dialect of C
but it wouldn't be Forth anymore.
I'm getting tired of this argument - it's pretty straight forward
that if you break the existing constructs of a language you end up
with a new language.

> > them stand out from normal words. Again I should point out the
> > similarities between defining and immediate words and
preprocessor
> > macros.
>
> You say this as if it were somehow ignored by me. As the author of
several
> Forth implementations, and one more coming shortly, I can assure
you that I

I've written a few Forth implementations myself - not a big deal -
Forth is one of the simplest languages to implement.

> know full well the differences and similarities between
preprocessor macros and
> immediate words. However, it is not at all relavent to the issue.
>

Well you seem to ignore what I said about colors. There is a reason
why Chuck Moore decided to make the defining words of different
color. It's because they do not behave like normal words. It is
because conceptually they are not the same as standard Forth words.
It is because they are generally executed at compile time and not run-
time.
Please read my first post again. You commented how CREATE does not
take it's input from the program but the input stream. In my reply I
stated that that I see defining words as a part of a separate
language that exists in symbiosis with Forth and has many
similarities with preprocessor macros. Chuck Moore seems to think the
same although he might not word it in the same way. So it is actually
very relevant to the issue.
If CREATE did take it's input from the program you'd be able to
create words at run-time and like I already stated the whole approach
to programming in this new Forth-like language would be different
(possibly somewhere half way between Joy and Forth or quite probably
very similar to what an older version of my programming language,
Meta/4, used to be like).

Ivan

Samuel Falvo — 2003-03-19 17:00:07

> Same thing happens with Forth, if you add words that modify or break
> the behaviour of existing words then that Forth interpreter/compiler
> won't be able to properly interpret other Forth programs. It's as
> simple as that.

You're begging the question here. I said *nothing* about changing the existing
functionality of ANSI-defined words.

> The more things you change/break, the less will the system behave

You are confusing "changing" with "adding." I never said that I'd get rid of
CREATE, :, VARIABLE, or other similar defining words. I *did* say that the
words *THESE WORDS DEPEND ON*, the words that accept a word's name given a
string on the stack (versus in the input stream), would be exposed to the
programmer. I cannot fathom as to how much more plain I can make this.

> like a standard Forth system. Making the dictionary a first class
> object and adding the ability to create words at run-time is a pretty
> big change - enough to break a whole bunch of programs previously

No it isn't. Forth does it all the time. In GForth:

see create

: Create
header reveal dovar: cfa, ; ok

see header

: input-stream-header
name name-too-short? header, ;
lastxt
Defer (header)
IS (header)

lastxt
Defer header
IS header ok

see header,

: header,
name-too-long? align here Last ! current @ 1 or , string, maxalign
alias-mask lastflags cset ; ok

What's wrong with exposing "header," to the programmer in a nice, portable
fashion? How can that possibly change the language so catastrophically as to
not be Forth anymore?

> written for that Forth system. Not to mention that programming in
> such a system would be completely different than normal Forth since
> you could create words that create other words at run-time and do all
> sorts of things that you just can't do with Forth. Sure it would be

Umm...EVALUATE?

S" : blah 1 2 + . ;" EVALUATE
blah

The interface is ugly, and therefore, grossly inconvenient when you want to
have a single word create a bunch of similarly defined words. But it *IS*
possible to do such things in modern Forth environments.

All my suggestion does is ease the interface for the programmer, so that string
manipulation, which Forth is patently poor at, doesn't need to happen.

> I'm getting tired of this argument - it's pretty straight forward
> that if you break the existing constructs of a language you end up
> with a new language.

Only,

(a) you haven't proved how my suggestion would break Forth's existing
constructs, and,

(b) you've attempted to change the argument via non-sequitors and begging the
question, and finally,

(c) I've proven conclusively above that you needn't change the semantics of the
language by exposing words which enable programmatic manipulation of the
dictionary.

At this point, I now believe you cannot establish such a proof, because one
does not exist.

> Well you seem to ignore what I said about colors. There is a reason

No, I directly addressed the issue of colors by bringing up modality in the
language. You apparently seemed to have forgotten that.

> Please read my first post again. You commented how CREATE does not
> take it's input from the program but the input stream. In my reply I
> stated that that I see defining words as a part of a separate
> language that exists in symbiosis with Forth and has many
> similarities with preprocessor macros. Chuck Moore seems to think the
> same although he might not word it in the same way. So it is actually
> very relevant to the issue.

Please read my first post on the subject again, where I distinctly said nothing
about *replacing* words like CREATE and :, but only *exposing* (that is,
*adding* to the programmer visible and documented wordset) the words that
enable programmatic manipulation of the dictionary. You seem to have missed
this very, very, very fundamental distinction in *ALL THREE* of the messages
that I've written on the subject.

--
Samuel A. Falvo II


__________________________________________________
Do you Yahoo!?
Yahoo! Platinum - Watch CBS' NCAA March Madness, live on your desktop!
http://platinum.yahoo.com

phimvt@lurac.latrobe.edu.au — 2003-03-20 00:49:40

On Tue, 18 Mar 2003, Samuel Falvo wrote:

> OK, here's the Oberon program I've written:

[.. translation from the Pascal version ..]

> While the syntax of the compiler fully supports the notation of recursively
> invoking inner functions, and passing them around as procedure pointers, the
> oo2c compiler does not permit this.

I feared that much.

> However, if oo2c were to implement
> trampolines like GCC does for references to inner procedures, then there's no
> reason why Oberon shouldn't support the feature. However, issues of
> type-safety come into play when doing this, which is presumably why Oberon
> doesn't permit this construction.

No, I do not think type-safety is the reason. After all, at least the
original DEC Pascal compilers did very rigorous type-checking. (For
an example - if you can stomach it - see the instance of fourth order
recursion in the Appendix part 4.) It has to do with passing on the
static chain, which is not trivial.

> Shucks. Oh well...

In case it is not entirely obvious: none of this should deter anyone
from implementing Joy in Oberon, since such an implementation would
not need backtracking at all.

- Manfred

phimvt@lurac.latrobe.edu.au — 2003-03-20 01:23:14

On Wed, 19 Mar 2003, Steven Shaw wrote:
> What is the amb function?

Actually I was careless - it is not a function but a special form
defined as a macro. A good exposition is in (the book ?)
Teach Yourself Scheme in Fixnum Days
find it by Google search: "scheme fixnum amb" (That's Ch 14).
Very briefly: (amb 1 42 7) returns 1 (and continues),
and if that fails it backtracks and returns 42 (and continues),
and if that fails it backtracks and returns 7 (and continues),
and eventually fails.

> > But I meant something different: using recursive calls with a local
> > procedure as a parameter to the recursive call. See the example at the
> > end.
>
> Pretty sure that schemers would just cons up a list to remember the odd
> numbers rather than using the runtime stack.

Yes, of course. And in Pascal one would use an array. The point
was to find *very* simple example of second order recursion.
There are more useful ones in the later chapters.

> However, it would be possible
> to port it to Scheme fairly intact. I gave it a try:
>
> ;-------------------------------------------------
> (define (display-it n) (display n) (display " "))
>
> ; similar to Pascal version of oddeven without the console input
> (define (odd-even list)
> (define (space) (display " "))
> (define (odd-dev list cp)
> (define n "?")
> (define (write-later) (cp) (display-it n))
> (if (null? list)
> (cp)
> (begin
> (set! n (car list))
> (if (even? n)
> (begin (display-it n) (odd-dev (cdr list) cp))
> (odd-dev (cdr list) write-later)))))
> (odd-dev list space))

Looks good. I take it the same thing would work in Common Lisp,
ML, Haskell ... ?

Also, in such languages in which functions are first class citizens
(unlike Pascal), it should be possible to eliminate the definition
of writelater, and replace its use (as a parameter) in the second last
line:
(odd-dev (cdr list) (lambda (cp) (display-it n)))))))
(or something like that). Would you care to try ?

[...]

Thanks for that.

- Manfred

Samuel Falvo — 2003-03-20 01:58:12

> No, I do not think type-safety is the reason. After all, at least the
> original DEC Pascal compilers did very rigorous type-checking. (For
> an example - if you can stomach it - see the instance of fourth order
> recursion in the Appendix part 4.) It has to do with passing on the
> static chain, which is not trivial.

Oberon's type safety is even stricter than that of Pascal. See the Oberon
Language Report. This is, in fact, why I declared the callback as a separate
type; had I not done so, Oberon's compiler would have complained about
mismatched types, despite "appearing" to be the same.

--
Samuel A. Falvo II


__________________________________________________
Do you Yahoo!?
Yahoo! Platinum - Watch CBS' NCAA March Madness, live on your desktop!
http://platinum.yahoo.com

Steven Shaw — 2003-03-20 13:39:07

> > What is the amb function?
>
> Actually I was careless - it is not a function but a special form
> defined as a macro. A good exposition is in (the book ?)
> Teach Yourself Scheme in Fixnum Days
> find it by Google search: "scheme fixnum amb" (That's Ch 14).
> Very briefly: (amb 1 42 7) returns 1 (and continues),
> and if that fails it backtracks and returns 42 (and continues),
> and if that fails it backtracks and returns 7 (and continues),
> and eventually fails.

Found it. Wow, it's pretty wierd. Thanks.

> > > But I meant something different: using recursive calls with a local
> > > procedure as a parameter to the recursive call. See the example at the
> > > end.
> >
> > Pretty sure that schemers would just cons up a list to remember the odd
> > numbers rather than using the runtime stack.
>
> Yes, of course. And in Pascal one would use an array. The point
> was to find *very* simple example of second order recursion.
> There are more useful ones in the later chapters.

Ok, no worries. Could you point me towards one of those examples?

> Looks good. I take it the same thing would work in Common Lisp,
> ML, Haskell ... ?

Yes. Common Lisp has first class functions but not first class
continuations.
ML is the same (but SML/NJ has call/cc). Not sure sure about
Haskell...getting it to output something is just so damn hard! I think it
would be harder to do in Haskell because the algorithm relies on
side-effects (outputting even numbers as you go). Could be wrong.

> Also, in such languages in which functions are first class citizens
> (unlike Pascal), it should be possible to eliminate the definition
> of writelater, and replace its use (as a parameter) in the second last
> line:
> (odd-dev (cdr list) (lambda (cp) (display-it n)))))))
> (or something like that). Would you care to try ?

Yes, that works.

Cheers,
Steve.

Serguey Zefirov — 2003-03-21 01:28:37

Hello Steven,

Thursday, March 20, 2003, 5:39:07 AM, you wrote:

>> Looks good. I take it the same thing would work in Common Lisp,
>> ML, Haskell ... ?

SS> Yes. Common Lisp has first class functions but not first class
SS> continuations.
SS> ML is the same (but SML/NJ has call/cc). Not sure sure about
SS> Haskell...getting it to output something is just so damn hard! I think it
SS> would be harder to do in Haskell because the algorithm relies on
SS> side-effects (outputting even numbers as you go). Could be wrong.

You may just use lazy evaluation in Haskell. And your pure functional
program can be a function from input String to output String. That's
the way old Haskell IO system worked.

Some time (about two year and half) ago I posted Haskell source for
Joy interpreter. I downloaded it from Yahoo Groups and after some
editing (yahoo broke the layout) it worked fine, giving me:

Joy> test
"test!"
1967 reductions, 4559 cells.

Look at it here:
http://groups.yahoo.com/group/concatenative/message/471
It is just core of interpreter encoded in Monad and lacks real Joy
syntax parser.

Actually, I still learning functional languages and programming, so I
can be wrong, but in lazy language you can encode ((in)effeciently)
continuation based program into more straightforward one (most of the
time;).

Right now I think I've try to encode it in the Fudgets stream
processors style:
http://www.cs.chalmers.se/Fudgets
to learn them better. ;)

And, after all, "getting it to output something is just so damn hard"
is Haskell greatest strength. ;)


--
Best regards,
Serguey mailto:sz@...

phimvt@lurac.latrobe.edu.au — 2003-03-24 04:27:14

On Thu, 20 Mar 2003, Steven Shaw wrote:

> > Yes, of course. And in Pascal one would use an array. The point
> > was to find *very* simple example of second order recursion.
> > There are more useful ones in the later chapters.
>
> Ok, no worries. Could you point me towards one of those examples?

http://www.latrobe.edu.au/philosophy/phimvt/s00bok.html
Chapter 8 for illustrations, then Ch 9 10 11 15 16 20 for real uses,
and Appendix part 4 "Fourth order recursion" for an extreme app.

> > Looks good. I take it the same thing would work in Common Lisp,
> > ML, Haskell ... ?
>
> Yes. Common Lisp has first class functions but not first class
> continuations.
> ML is the same (but SML/NJ has call/cc). Not sure sure about
> Haskell...getting it to output something is just so damn hard! I think it
> would be harder to do in Haskell because the algorithm relies on
> side-effects (outputting even numbers as you go). Could be wrong.

Use a list [1 2 3 4 5 6 7 8 9] as input, produce a list
with separator [1 3 5 7 9 999999 2 4 6 8] as output.
Or maybe the reverse (obtained by consing) would be
easier: [8 6 4 2 999999 9 7 5 3 1]. Only a "proof of concept"
is called for here.

> > (unlike Pascal), it should be possible to eliminate the definition
> > of writelater, and replace its use (as a parameter) in the second last
> > line:
> > (odd-dev (cdr list) (lambda (cp) (display-it n)))))))
> > (or something like that). Would you care to try ?
>
> Yes, that works.

Thanks.

- Manfred

Steven Shaw — 2003-03-24 07:11:00

> > Ok, no worries. Could you point me towards one of those examples?
>
> http://www.latrobe.edu.au/philosophy/phimvt/s00bok.html
> Chapter 8 for illustrations, then Ch 9 10 11 15 16 20 for real uses,
> and Appendix part 4 "Fourth order recursion" for an extreme app.

Thanks. The entire book looks very interesting!

Steven Shaw — 2003-03-24 15:46:39

> > Yes. Common Lisp has first class functions but not first class
> > continuations.
> > ML is the same (but SML/NJ has call/cc). Not sure sure about
> > Haskell...getting it to output something is just so damn hard! I think
it
> > would be harder to do in Haskell because the algorithm relies on
> > side-effects (outputting even numbers as you go). Could be wrong.
>
> Use a list [1 2 3 4 5 6 7 8 9] as input, produce a list
> with separator [1 3 5 7 9 999999 2 4 6 8] as output.
> Or maybe the reverse (obtained by consing) would be
> easier: [8 6 4 2 999999 9 7 5 3 1]. Only a "proof of concept"
> is called for here.

I mangaged to get it going in Haskell both using side effects (outputting as
it goes) and returning a list as you suggested. To my surprise it turned out
to be easy to implement with side effects. I tried a couple of different
techniques for functions returning lists but I don't think these functions
maintain the "second order recursion".

Here is the side effecting version using second order recursion:

oddEven2 list = let
aux (x:xs) cp =
if (even x) then
do output (show x); aux xs cp
else
aux xs (do cp; output (show x))
aux [] cp = cp
in do
aux list (output "....")
out "\n"

The main problem with Haskell seems to be obsure error messages (always
typing errors) often far away from the cause. In versions creating lists, I
found that I couldn't cons things together with :: but instead had to use
string append (++) because of some typing error (that probably leads to
poorer performance).

Steve.


[Non-text portions of this message have been removed]

Steven Shaw — 2003-03-24 16:14:16

Here is the full code as the attachment was deleted:

--Main.hs ------------------------------------------------------------------
--
import IO

out = hPutStr stdout

output x = do out x; out " "

foreach (x:xs) f = do f x; foreach xs f
foreach [] f = out "" -- how to do nothing?

main =
let list = [1..9]
in do
out "list with print = "; print list
out "list with foreach = "; foreach list (\x -> output (show x)); out
"\n"
out "version 1 = "; print (oddEven1 list)
out "version 2 = "; oddEven2 list
out "version 3 = "; print (oddEven3 list)
out "version 4 = "; oddEven4 list

-- not using recursive local functions, returns list
oddEven1 list = let
aux (x:xs) odds =
if (even x) then
[x] ++ (aux xs odds)
else
(aux xs ([x] ++ odds))
aux xs odds = [999999] ++ (reverse odds)
in aux list []

-- uses recursive local functions, outputing as it goes
oddEven2 list = let
aux (x:xs) cp =
if (even x) then
do output (show x); aux xs cp
else
aux xs (do cp; output (show x))
aux [] cp = cp
in do
aux list (output "....")
out "\n"

-- like oddEven1 but in same style as the recursive local
oddEven3 list = let
aux (x:xs) cp =
if (even x) then
[x] ++ (aux xs cp)
else
aux xs (cp ++ [x])
aux [] cp = cp
in aux list [999999]

-- remembers the odds, outputs as it goes
oddEven4 list = let
aux (x:xs) odds =
if (even x) then
do output (show x); aux xs odds
else
aux xs (odds ++ [x])
aux xs odds = do
output "...."
foreach odds (\x -> output (show x))
in do
aux list []
out "\n"

----------------------------------------------------------------------------
--


Here is the output produced:

----------------------------------------------------------------------------
--
list with print = [1,2,3,4,5,6,7,8,9]
list with foreach = 1 2 3 4 5 6 7 8 9
version 1 = [2,4,6,8,999999,1,3,5,7,9]
version 2 = 2 4 6 8 .... 1 3 5 7 9
version 3 = [2,4,6,8,999999,1,3,5,7,9]
version 4 = 2 4 6 8 .... 1 3 5 7 9
----------------------------------------------------------------------------
--

John Cowan — 2003-03-24 18:21:28

phimvt@... scripsit:

> PROGRAM oddevn(input,output);

I translated the program into GNU C (it will not work in ANSI C, of course):

===== cut here =====

# include <stdio.h>

typedef void (*proc)();

int eoln() {
int i;
i = getchar();
if (i == '\n') return 1;
ungetc(i, stdin);
return 0;
}

int eof() {
int i;
i = getchar();
if (i == EOF) return 1;
ungetc(i, stdin);
return 0;
}

void space() {
printf(" ");
}

void odev(proc cp) {
int n;

void writelater() {
cp();
printf("%d ", n);
}

if (eoln()) cp();
else {
scanf("%d", &n);
if (n % 2 == 0) {
printf("%d ", n);
odev(cp);
}
else
odev(writelater);
}
}

int main() {
while (!eof()) {
odev(space);
printf("\n");
}
}

===== cut here =====

> It would be of interest to me to know whether gcc can handle
> this sort of thing (as I mentioned before, the version of p2c
> that I once tried failed.)

It works, most definitely. It took a bit of debugging to get right, basically
because of oversights in my hand-translation, and I had to research the
semantics of eoln and eof in Pascal to realize that they involve lookahead.

--
In politics, obedience and support John Cowan <jcowan@...>
are the same thing. --Hannah Arendt http://www.ccil.org/~cowan

phimvt@lurac.latrobe.edu.au — 2003-03-27 05:47:28

On Mon, 24 Mar 2003, John Cowan wrote:

> phimvt@... scripsit:
>
> > PROGRAM oddevn(input,output);
>
> I translated the program into GNU C (it will not work in ANSI C, of course):
>
> ===== cut here =====
[...]
> ===== cut here =====
[...]

> It works, most definitely. It took a bit of debugging to get right, basically
> because of oversights in my hand-translation, and I had to research the
> semantics of eoln and eof in Pascal to realize that they involve lookahead.

Thank you very much. This will save me from teaching my students
the other two (very ugly) methods that I eventually got to work
for translating my backtracking programs in Pascal into (K&R)C:
1) [unportable, I am sure] pass on an extra parameter which is the
beginning of the current activation record, or
2) [portable, but combersome] an explicit stack of continuations.
Also, it gives me some motivation to translate the later ones
into GCC ... (they are too large to expect my students to do them).

Thanks again.

- Manfred

Samuel Falvo — 2003-03-27 07:43:54

> 2) [portable, but combersome] an explicit stack of continuations.

I'm curious if this realization is what led to your research in creating Joy?
That is, if you're going to manipulate a stack of continuations manually and
explicitly, why not design the whole langauge around it? Just wondering.

--
Samuel A. Falvo II


__________________________________________________
Do you Yahoo!?
Yahoo! Platinum - Watch CBS' NCAA March Madness, live on your desktop!
http://platinum.yahoo.com

phimvt@lurac.latrobe.edu.au — 2003-04-01 03:26:30

On Wed, 26 Mar 2003, Samuel Falvo wrote:

> > 2) [portable, but combersome] an explicit stack of continuations.
>
> I'm curious if this realization is what led to your research in creating Joy?

No, no connection at all. In fact, the prehistory of Joy
is mercifully shrouded in the mists of time.

- Manfred