ANNOUNCE: Joy-in-Scheme version 0.80 (no Scheme required; please test!)

John Cowan — 2006-05-29 16:43:49

I'm announcing version 0.80 of Joy-in-Scheme, eventually to become Joy2.
You do not need a Scheme system to run this version; it should work
anywhere that there is a C compiler. I'd like as many different people
to test it as possible.

Download it, say "touch joy80.c", and then "make". You may need to
fiddle with the Makefile a bit, but it should be simple.

It implements about 80% of the Joy primitives. However, the input
routine is very limited compared to standard Joy; in particular:

To evaluate Joy code, type the words followed by "!" by itself
("." will not work, nor will "!" attached to the end of a word)

To define a Joy word, use "DEFINE newword == words ... !", again
with "!" by itself. LIBRA, MODULE, HIDE, etc. will not work;
multiple definitions will not work.

These restrictions will be lifted before this version is complete,
and the other primitives will be added.

If you have Chicken Scheme installed on your system, you can run Joy in
the interpreter using "csi joy80.scm" and then "(joy-repl)".

Here are the primitives that aren't implemented yet:

echo localtime gmtime mktime strftime format formatf include
feof ferror fread fwrite fseek ftell compare opcase case set
setecho ifset condlinrec fold primrec filter split some all
treestep treerec treegenrec help _help helpdetail manual abort

In addition, sets are not supported at all.

--
I suggest you call for help, John Cowan
or learn the difficult art of mud-breathing. cowan@...
--Great-Souled Sam http://www.ccil.org/~cowan

John Cowan — 2006-05-29 16:51:30

I should add that Joy comments don't work either.

--
John Cowan cowan@... http://ccil.org/~cowan
If I have not seen as far as others, it is because giants were standing
on my shoulders.
--Hal Abelson

Greg Buchholz — 2006-05-29 17:43:09

--- John Cowan <cowan@...> wrote:
> I'm announcing version 0.80 of Joy-in-Scheme

URL?

Greg Buchholz

__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com

John Cowan — 2006-05-29 18:33:49

Greg Buchholz scripsit:

> --- John Cowan <cowan@...> wrote:
> > I'm announcing version 0.80 of Joy-in-Scheme
>
> URL?

/me facepalms.

http://www.ccil.org/~cowan/joy80.tar.gz

Sorry about that.

--
He played King Lear as though John Cowan <cowan@...>
someone had played the ace. http://www.ccil.org/~cowan
--Eugene Field

Manfred Von Thun — 2006-06-23 09:21:38

I am sorry I have not been able to take part in the interesting
discussions on the group. I have emptied my house of 34 years,
disposed of accumulated junk and not quite junk, moved to a
much smaller rented flat, instructed an estate agent to sell my
house ­ and I am exhausted!

I am reading the group mail every one or two weeks, but I am far
behind in doing anything but the most superficial. Just unpacked
and started reading John¹s Joy in Scheme. It sure looks better
than my original in C. Well done John, I do hope people will
try it out ­ as I cannot at the moment.

I expect to be more active in a little while. Best wishes to all.

- Manfred

On 30/5/06 4:33 AM, "John Cowan" <cowan@...> wrote:

> Greg Buchholz scripsit:
>
>> > --- John Cowan <cowan@...> wrote:
>>> > > I'm announcing version 0.80 of Joy-in-Scheme
>> >
>> > URL?
>
> /me facepalms.
>
> http://www.ccil.org/~cowan/joy80.tar.gz
>
> Sorry about that.




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

Manfred Von Thun — 2006-10-13 07:56:22

On 30/5/06 2:43 AM, "John Cowan" <cowan@...> wrote:

> I'm announcing version 0.80 of Joy-in-Scheme, eventually to become Joy2.
> You do not need a Scheme system to run this version; it should work
> anywhere that there is a C compiler. I'd like as many different people
> to test it as possible.
>
> Download it, say "touch joy80.c", and then "make". You may need to
> fiddle with the Makefile a bit, but it should be simple.
[..]

Hi John,

after many months in limbo while being forced off my beloved mainframe, my
little Apple eMac now has a new version of the operating system and a GCC. I
was told that I am the first person in our Humanities faculty to ever even
ask for a C-compiler. Anyhow, it all happened eventually, and today I used
the compiler for the first time ‹ of course on your Joy-in-Scheme. I had
already commented on the Scheme code that you put together so carefully, I
found it quite intelligible even on first reading. Now I compiled it at
last, just ³make², and bingo! there it was. I ran a few simple tests just
now, and all seems to be working. I hope that you will not give up on this
project, with the lack of interest and encouragement so far.

At this stage I have one question. It is prompted by some of the features of
your version ­ especially the ³!² instead of ³.² as a terminator (not that
it matters which one is used). But I get the impression that writing the
scanner in Scheme is not as straightforward as one might imagine, and that
Scheme is not the best character-fiddling language there is. Is this so?
Related to this, would it be desirable ­ and possible - to write the really
low-level parts of Joy in C and only the high-level parts in Scheme? Where
one should draw the line might well be decided on pragmatic grounds.

As part of the answer to my own question I offer this: whatever needs
a garbage collector will need to be written in Scheme ­ and that is mainly
what in my implementation is in the file interp.c.

I also noticed that somewhere your parser reads code by consing it onto a
list, but then reverses that list to get it in the right order again for
execution. I am not talking about efficiency. In my version the parser kept
a pointer to the last item and kept on appending there. I would have thought
that Scheme¹s setcdr! is just for that sort of thing. Or is there some deep
reason for using the reversal technique?

I look forward to studying your implementation in much more detail.
(And I now want to compile my own Joy with my brand new GCC)

Thanks John

- Manfred











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

John Cowan — 2006-10-13 16:18:03

Manfred Von Thun scripsit:

> I hope that you will not give up on this
> project, with the lack of interest and encouragement so far.

I have not given up, but I did suspend work on it until I had some
detailed review.

> But I get the impression that writing the
> scanner in Scheme is not as straightforward as one might imagine, and that
> Scheme is not the best character-fiddling language there is. Is this so?

Not at all. I simply wanted to push something at least partly usable
out there, and I had stopped working on 0.80 a month or two before.
(My bad luck that when I published it was the beginning of when you
would be unable to review or work on it.) The simplest, most expeditious
solution was therefore to simply exploit the Scheme scanner and parser,
even taking advantage of the fact that Chicken's scanner supports []
as equivalent to ().

> Related to this, would it be desirable ­ and possible - to write the really
> low-level parts of Joy in C and only the high-level parts in Scheme? Where
> one should draw the line might well be decided on pragmatic grounds.

Possible but not I think desirable. Chicken makes it easy to interface
with low-level C code, even embedded directly in Scheme (though of course
only in compiled code -- the interpreter does not understand C!).
But Scheme is in fact an excellent language for writing scanners
and parsers; notably, its guarantee of proper tail recursion makes
recursive-descent scanners particularly nice.

> I also noticed that somewhere your parser reads code by consing it onto a
> list, but then reverses that list to get it in the right order again for
> execution. I am not talking about efficiency. In my version the parser kept
> a pointer to the last item and kept on appending there. I would have thought
> that Scheme¹s setcdr! is just for that sort of thing. Or is there some deep
> reason for using the reversal technique?

Again, a matter of expeditiousness. Since this part of the implementation
is due to be replaced, I didn't bother to construct a proper queue.

> I look forward to studying your implementation in much more detail.

Please do!

--
There are three kinds of people in the world: John Cowan
those who can count, cowan@...
and those who can't.

Manfred Von Thun — 2006-10-23 09:48:58

A little preamble: one of the criticisms of Pascal was that it has some
builtin procedures have features which cannot be given to user defined
procedures - example the write statement (write(a,b,c,d,e)) can take any
number of arguments, but you cannot define your own procedures that can have
any number of arguments. The printf statement in C is similar. Joy commits
the same kind of sin: some combinators, like map and filter traverse a list
left to right, building another list left to right by appending to the end
(as a queue). But user defined combinators cannot do this (at least not in a
clean fashion), because lists (and quotations) are singly linked starting at
the first element. I have often thought that perhaps Joy¹s lists should be
circular ­ a pointer to the last element, which in turn contains a pointer
to the first element. That way first and last are (almost) equally
accessible and modifyable ­ admittedly, perhaps destroying the purely
functional nature of Joy.


On 14/10/06 2:18 AM, "John Cowan" <cowan@...> wrote:

>
> Manfred Von Thun scripsit:
>
> [..]
>
>> I also noticed that somewhere your parser reads code by consing it onto a
>> list, but then reverses that list to get it in the right order again for
>> execution. I am not talking about efficiency. In my version the parser kept a
>> pointer to the last item and kept on appending there. I would have thought
>> that Scheme¹s setcdr! is just for that sort of thing. Or is there some deep
>> reason for using the reversal technique?
>>
> Again, a matter of expeditiousness. Since this part of the implementation is
> due to be replaced, I didn't bother to construct a proper queue.
>
The parser is one of many functions which could benefit from circular lists,
and presumably users could find all sorts of other uses in which lists need
to be constructed from first to last, allowing something like setcdr! or
whatever. Perhaps the functional nature of Joy is not entirely destroyed if
the use of such operators is restricted in some way. I realise that circular
lists are a just a little harder to implement, and that there will also be
an efficiency penalty.

To your implementation: I think this is a good opportunity to think about
extending Joy perhaps with some new concepts. The Joy manual is not carved
in granite, and much of Joy needs serious revision anyhow. In particular I
think my design of the libraries was rather ad hoc and pretty amateurish. I
should have modelled them on libraries in well-established languages. With
your knowledge of Scheme (including its impure features) you might well have
some thoughts about how Joy could benefit from adopting some of the pure and
impure features. It would not bother be in the slightest if your
implementation departs from mine in quite substantial ways, and takes over
as the principal implementation. I have always said that I welcome
experimentation, and many members of the concatenative mailing group have
taken that to heart with all sorts of variant concatenative languages.

I look forward to hearing your views, John, and the views of others.

- Manfred


>

John Cowan — 2006-10-23 15:25:58

Manfred Von Thun scripsit:

> A little preamble: one of the criticisms of Pascal was that it has some
> builtin procedures have features which cannot be given to user defined
> procedures - example the write statement (write(a,b,c,d,e)) can take
> any number of arguments, but you cannot define your own procedures that
> can have any number of arguments. The printf statement in C is similar.

No longer true of C, BTW; ANSI C allows you to write your own
variadic functions in a standards-conformant fashion.

> I have often thought that perhaps Joy's lists should be circular --
> a pointer to the last element, which in turn contains a pointer
> to the first element. That way first and last are (almost) equally
> accessible and modifyable -- admittedly, perhaps destroying the purely
> functional nature of Joy.

I think that would be too big a loss for a modest gain in efficiency.
Stop-and-copy garbage collectors (like Chicken's) make it fairly cheap
to create transient garbage, as it will never be copied or otherwise
looked at again -- at worst the GC runs more often.

I think Joy's statelessness (except for I/O) is part of its essence,
being what assures the purity of its concatenativeness.

> To your implementation: I think this is a good opportunity to think
> about extending Joy perhaps with some new concepts. The Joy manual
> is not carved in granite, and much of Joy needs serious revision
> anyhow. In particular I think my design of the libraries was rather ad
> hoc and pretty amateurish. I should have modelled them on libraries in
> well-established languages. With your knowledge of Scheme (including
> its impure features) you might well have some thoughts about how Joy
> could benefit from adopting some of the pure and impure features. It
> would not bother be in the slightest if your implementation departs
> from mine in quite substantial ways, and takes over as the principal
> implementation. I have always said that I welcome experimentation,
> and many members of the concatenative mailing group have taken that
> to heart with all sorts of variant concatenative languages.

I'd like to concentrate on getting Joy1 ported first, and would
welcome any assistance in doing so. I'm not too clear on the
appropriate implementation of some of the combinators, for example.

There is a very interesting nearly-functional language called Q
<http://q-lang.sourceforge.net> which is based on equational term
reasoning rather than lambda calculus. I've recently provided a Chicken
interface to the Q interpreter; it'd be interesting to make that
accessible from Joy.

Here's a tiny example that puts symbolic Boolean algebra expressions
into disjunctive normal form (capitalized identifiers are variables):

/* dnf.q: This is a tiny example for symbolic expression manipulation. It
transforms logical expressions into disjunctive normal form (DNF).
05-08-1993 AG */

// eliminate double negations:
not not A = A;

// push negations inward (de Morgan's laws):
not (A or B) = not A and not B;
not (A and B) = not A or not B;

// distributivity laws:
A and (B or C) = A and B or A and C;
(A or B) and C = A and C or B and C;

// associativity laws:
A and (B and C) = (A and B) and C;
A or (B or C) = (A or B) or C;

--
He made the Legislature meet at one-horse John Cowan
tank-towns out in the alfalfa belt, so that cowan@...
hardly nobody could get there and most of http://www.ccil.org/~cowan
the leaders would stay home and let him go --H.L. Mencken's
to work and do things as he pleased. Declaration of Independence

Manfred Von Thun — 2006-10-31 09:13:00

On 24/10/06 1:25 AM, "John Cowan" <cowan@...> wrote:

> [..]

> I'd like to concentrate on getting Joy1 ported first, and would
> welcome any assistance in doing so. I'm not too clear on the
> appropriate implementation of some of the combinators, for example.
>
I am still studying your Scheme version, with some envy. For example
your joy-stable function is just what I should have written as a macro
or something to eliminate many duplications in the code of the
interpreter.

Currently I have been looking at your implementation of step
and map, and presumably the implementation of fold and filter
end up looking pretty much the same. I think you are right in
making, for X = step,map,(fold,filter), a function

> (joy-prim-void (X a p) ...

which inspects the type (string/list) of parameter a and
then dispatches to specialised version joy-X-string or joy-X-list.
That is useful because you need recursion at least for some of them.
At any rate, this is how I understood your code.

I did not need to do that for these simple list processing
combinators because I was able to use loops, always
appending something to the end in the case of the result list
for map and filter. However, I did need some auxiliary functions
for the more contorted combinators like treerec and nestrec.

But my studying is hampered by the fact that I come in to
my office at most twice a week, and I have quite a few
things to do in very short time. So please be patient with
me; but you should also recognise that so far Scheme has
been little more that a spectator sport for me.

Maybe there is some Schemer in the concatenative group
who is willing to contribute to your Scheme implementation?

- Manfred



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

John Cowan — 2006-11-01 00:06:02

Manfred Von Thun scripsit:

> But my studying is hampered by the fact that I come in to
> my office at most twice a week, and I have quite a few
> things to do in very short time. So please be patient with
> me; but you should also recognise that so far Scheme has
> been little more that a spectator sport for me.

/me delicately suggests a printout.

> Maybe there is some Schemer in the concatenative group
> who is willing to contribute to your Scheme implementation?

That would be great. So far, I haven't been able to interest
anyone.

--
Income tax, if I may be pardoned for saying so, John Cowan
is a tax on income. --Lord Macnaghten (1901) cowan@...

Manfred Von Thun — 2006-11-06 05:36:17

I have been thinking about Joy in Scheme, especially how to
write Scheme code for the combinators. Here is what I came
up with for the map combinator. I am supposing that we
already have a Scheme function which does the equivalent
of ³exeterm² in my Joy-in C. Its type is
> exeterm: quotation * stack -> stack
This means it takes two parameter, a quotation and a stack
(really the remainder of what is below the quotation) as
parameters and produces a stack as result. The body of
exeterm must do this: step through the quotation, look
at each item ­ if it is a literal (number, char, list ..) push
if onto the stack, if it is an inbuilt, call that code, if it is
user defined, look up its body and exeterm that.

For map we need a Scheme function which checks that
there is a quotation on top of the stack, and below that
an aggregate, and possibly below that further items.
It has the type
> map: stack -> stack
Very roughly the code will look like this:

(define (map s)
>> ( here check that there are two items on stack
>> and check that top is a quotation )
>> (cond
>>> ((= (type (second s)) list)
>>> (cons (maplist (first s) (second s) (rest (rest s))))
>>> ((= (type (second s)) string)
>>> ( call the special function for mapping strings)
>>> ((= (type (second s)) set)
>>> ( call the f for mapping sets)
>>> (default
>>> ( error ³bad aggregate for map²) ) ) )

Pardon my Scheme, it has been 10 years since I wrote my
one an only 10-liner in Scheme. If some common Lisp
has crept in, sorry.

The maplist function has the type
> maplist: quotation * list * stack -> stack
where the stack parameter is the remainder of the stack. The others,
mapstring and mapset, would have a similar type. Whether
these others should be recursive I do not know. But certainly
maplist is most easily written recursively.

I start with a version (untested) for ordinary map in
Scheme, which I think looks approximately like this:
>
> map: function * list -> list
>
> (define (map f l)
>> (if
>> (null l)
>> nil
>> (cons
>> (f (first l))
>> (map (f (rest l)) ) ) )
>>>>
Finally, I use this outline to write that maplist needed
for Joy-in-Scheme, by making just a few changes:

> (define (maplist q l r)
>> (if
>>> (null l)
>>>> (cons nil r)
>>> (cons
>>>> (first (exeterm q (cons (first l) r)))
>>>> (maplist q (rest l) r) ) ) )

Explanation of the interesting lines, contrasted with
the lines on the ordinnary map for Scheme.

(cons nil r)
> instead of returning just nil,
> push nil onto the (remainder of)
> the stack, and return that.

(first (exeterm q (cons (first l) r)))
> instead of taking the first of l,
> push the first of l onto the remainder r.
> Instead of calling a function f,
> call exeterm with the quotation
> and the augmented stack.
> When that¹s done, take the first
> of the result stack as the first argument
> for cons.

(maplist q (rest l) r) ) ) )
> instead of a recursive call with just the
> rest of the list, do a recursive call with
> the rest of the list and the remainder of
> the stack. The result is a list, the second
> argument for the cons.
>
There is still something not quite right: If the
quotation q does some IO, then this should be
guaranteed to be in the order of the list elements.
That would be ok BUT ONLY IF Scheme evaluates
the arguments of cons in a left to right sequence.
I doubt whether Scheme does strict left to right
evaluation, somehow it is not the sort of thing
that goes with the purely functional core of Scheme.

To fix it one needs to ask explicitly for sequential
execution. So, assign the mapped value of the first
of the list to a local variable v, then do the cons
with the result of the recursive call.
I can¹t remember how that is done in
Scheme, so I¹ll write it my way:

> (define (maplist q l r)
>> (if
>>> (null l)
>>>> (cons nil r)
>>> (begin
>>> (let v (first (exeterm q (cons (first l) r)))
>>> (cons
>>>> v
>>>> (maplist q (rest l) r) ) ) )

It looks a mess, but I know that Scheme programmers are
kind people who will put up with that sort of thing.

I hope this will make things clearer. If required I shall
write similar pseudo-Scheme clarifications for other
combinators.

- Manfred
>>>>
>>>>



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