Re: [stack] Re: Fw: ANNOUNCE: enhancements to JOY

John Cowan — 2001-06-26 19:13:53

Jack Waugh wrote:

> The announcement in comp.lang.functional (archived, by the way, at
> http://groups.google.com/groups?
> q=Joy&hl=en&group=comp.lang.functional&safe=off&rnum=1&ic=1&selm=9g9sp
> u%247c0%241%40latcs1.cs.latrobe.EDU.AU if you can get all that into
> your browser) promises "Files as a datatype, together with some
> common functions for manipulating files and doing I/O." Are the
> files now gone from function arguments and results (other than any
> files that might happen to occupy positions in the stack)?

I have no clue what this means.


--
There is / one art || John Cowan <jcowan@...>
no more / no less || http://www.reutershealth.com
to do / all things || http://www.ccil.org/~cowan
with art- / lessness \\ -- Piet Hein

Manfred von Thun — 2001-06-27 02:34:09

I thought that by now I would have sorted out most bugs in
at least the core parts of Joy. But here is one that has me
stumped - it occurs in both Joy0 and Joy1:

true not.
false OK
true dup and.
true OK
[5] [dup *] map.
[25] OK

[true] [not] map.
run time error: different type needed for not
[true] [dup and] map
run time error: different type needed for and

Same strange message for "false" instead of "true",
and for "or" instead of "and". Same message for
longer lists of logical values. But empty list is OK:

[] [not] map
[] OK
[] [dup and] map
[] OK

I'm stumped. I am still busy finalising marks for the
semester, so I won't be able to attend to this bug for
a while. In the meantime ... (:-)
- Manfred

Louis Madon — 2001-06-27 09:00:38

I had a quick look and it seems the problem is that the list contains
instances of the _function_ 'true' rather than its value. ie. the same
kind of thing that you get when you do '[cons] first' for example. If
you create a little helper function like this:

bare_i == [] cons i


then you can do:

[true] [bare_i not] map
[false] OK


this also explains why doing 'true not' is ok. 'true' gets executed
first - leaving a boolean on the stack - and only then does 'not'
execute.


I don't know the C code that well, but I think that to get the same
semantics as for other literals (numbers, strings, etc) it is neccesary
for true and false to also be handled specially when read. For other
functions (cons, pop, etc) getting a bare function is what you want so
the existing semantics should not be changed for them.

I hope this helps,
Louis.


On Wednesday, June 27, 2001, at 12:34 PM, Manfred von Thun wrote:

> I thought that by now I would have sorted out most bugs in
> at least the core parts of Joy. But here is one that has me
> stumped  -  it occurs in both Joy0 and Joy1:
>
> true  not.
> false                        OK
> true  dup and.
> true                         OK
> [5]  [dup *] map.
> [25]                         OK
>
> [true]  [not] map.
>   run time error: different type needed for not
> [true]  [dup and] map
>   run time error: different type needed for and
>
> Same strange message for "false" instead of "true",
> and for "or" instead of "and". Same message for
> longer lists of logical values. But empty list is OK:
>
> []  [not] map
> []                           OK
> []  [dup and] map
> []                           OK
>
> I'm stumped. I am still busy finalising marks for the
> semester, so I won't be able to attend to this bug for
> a while.  In the meantime ... (:-)
>   - Manfred


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

John Cowan — 2001-06-29 22:01:58

Louis Madon wrote:

> I had a quick look and it seems the problem is that the list contains
> instances of the _function_ 'true' rather than its value.


Absolutely right. This is a very fundamental issue in Joy:
even if DEFINE five == 5, the lists [five five five] and [5 5 5]
are very different things. The list [true true true] is like
the former; there is no boolean equivalent of the latter.

Manfred, I think we need to regularize the policy here, but
I'm not sure what the Right Thing might be.

--
There is / one art || John Cowan <jcowan@...>
no more / no less || http://www.reutershealth.com
to do / all things || http://www.ccil.org/~cowan
with art- / lessness \\ -- Piet Hein

Manfred von Thun — 2001-07-02 01:49:20

On Wed, 27 Jun 2001, Louis Madon wrote:

> I had a quick look and it seems the problem is that the list contains
> instances of the _function_ 'true' rather than its value. ie. the same
> kind of thing that you get when you do '[cons] first' for example. If
> you create a little helper function like this:
>
> bare_i == [] cons i
>
>
> then you can do:
>
> [true] [bare_i not] map
> [false] OK
>
>
> this also explains why doing 'true not' is ok. 'true' gets executed
> first - leaving a boolean on the stack - and only then does 'not'
> execute.
>
>
> I don't know the C code that well, but I think that to get the same
> semantics as for other literals (numbers, strings, etc) it is neccesary
> for true and false to also be handled specially when read. For other
> functions (cons, pop, etc) getting a bare function is what you want so
> the existing semantics should not be changed for them.
>
> I hope this helps,
> Louis.
Thank you very much, Louis, your analysis is exactly right.
I am most embarassed about writing wrong code like that,
not noticing the mistake earlier, and not seeing where the
problem lies. I thought it was specific to map, but actually
it also arises for filter, split and fold, and maybe others.
Here is a temporary quick fix:

1. in file scan.c (towards the very end)
in function getsym()
in the default clause
just after the two lines
if (strcmp,id,"==") == 0)
{ sym = EQDEF; return; }
insert the four lines
if (strcmp(id,"true") == 0)
{ sym = BOOLEAN_; num = 1; return; }
if (strcmp(id,"false") == 0)
{ sym = BOOLEAN_; num = 0; return; }
2. in file utils.c
in function readfactor()
to the line
case INTEGER_: case CHAR_: case STRING_:
prefix another case -
case BOOLEAN_:
3. The two changes above are adequate, but if this solution is
made permanent then hygene demands additionally
a) delete the two "true" and "false" calls to the PUSH(..) macro
b) change the optable entry so that true_ and false_ cannot be called
at all; use dummy_ instead.

BUT - although this is OK as a fix, I am not so sure I like it:
1. Similar fixes are needed for the half dozen other calls to
the PUSH(..) macro, for maxint, setsize etc
If all those calls are deleted, the whole macro should go.
2. It is inelegant and unsafe. Ideally the strings "true" and "false"
(and the half dozen others) should occur only once in the entire
implementation. (Imagine changing to German Joy ("Freude"?):
"wahr" and "falsch" now have to be used in several places.)
It is also slightly inefficient to have that long chain of
special case if-then's in the C-code for scan.c before the
most common default clause is reached.
3. A better solution would be to have a single mechanism for all
for all these case, possibly somewhere inside file utils.c
function readfactor. Of course the information as to what is
and is not a special case will have to be found somewhere.
My current thinking is that perhaps that sort of thing should
be in file interp.c, declaration of optable, by having additional
fields. Currently there is a string field for the spelling,
used for reading and writing, a function field for executing,
and two further string fields for help. Additional fields
could be useful for all sorts of things - like arities for
operators and combinators.
But I am still thinking about all this.

Again, thank you Louis,

- Manfred

Manfred von Thun — 2001-07-02 02:29:54

On Fri, 29 Jun 2001, John Cowan wrote:

> Louis Madon wrote:
>
> > I had a quick look and it seems the problem is that the list contains
> > instances of the _function_ 'true' rather than its value.
>
>
> Absolutely right. This is a very fundamental issue in Joy:
> even if DEFINE five == 5, the lists [five five five] and [5 5 5]
> are very different things. The list [true true true] is like
> the former; there is no boolean equivalent of the latter.
>
> Manfred, I think we need to regularize the policy here, but
> I'm not sure what the Right Thing might be.

I'm not at all sure myself. You will have seen my quick fix
to the problem and my doubts about it in my reply to Louis.
Until you and others have looked at it I won't change the
C-code on the web page, and I suggest you don't incorparate
these changes into the joy.tar.gz file yet.
I've only had one more thought on it all, consider this:

DEFINE
five == 5;
fourth == pop pop pop;
third == pop pop;
bare_i == [] cons i.

bare_i is what Louis called it. I have sometimes thought that perhaps
there should be a primitive of that kind, useable for example in
(the second draft of) the Joy-in-Joy interpreter.

10 20 30 40 50 [6 five fourth third] [bare_i dup *] map ==
10 20 30 40 50 [36 25 400 900]

Yes, the 10..50 below the list are preserved in spite of all the pops.
I think this is good behaviour, and should stay like that. On the other
hand, we do (and should have)
[five fourth third John] reverse ==
[John third fourth five]
without evaluation. That means that none of the elements in the
last list should get evaluated, because John is undefined, and cannot be
evaluated, and third and third and fourth cannot be evaluated
when there is nothing like 10..50 below on the stack.
So five should not be evaluated either.

On the other hand, all the aggregate combinators (map,fold,filter,split..)
when applied to list aggregates could do with a "bare_i" version,
which enforces the evaluation, e.g.
bare-i-map == [[] cons i] swoncat map
Alternatively, have a unary list operator
evaluate == [[] cons i] map
to be used in
10 20 30 40 [five 6 third] evaluate ==
10 20 30 40 [5 6 20]
and then define
bare-i-map == [evaluate] dip map

The upshot of this is that the only atoms that should be evaluated
as the get into the list are the inbuilt CONSTANTS: true, false,
maxint, setsize etc., and that user defined functions like five,
fourth and third should not be evaluated.

Maybe we should also reconsider your earlier on names
insider sets.

Comments most welcome
- Manfred

John Cowan — 2001-07-02 04:00:44

Manfred von Thun scripsit:

> The upshot of this is that the only atoms that should be evaluated
> as the get into the list are the inbuilt CONSTANTS: true, false,
> maxint, setsize etc., and that user defined functions like five,
> fourth and third should not be evaluated.

Yes, on reflection I agree with this: there should be no built-in constant
functions in Joy, only constants with various spellings.
So 5 and true should be on the same level.
This will take some work to get right.

> Maybe we should also reconsider your earlier on names
> inside sets.

Yes.

--
John Cowan cowan@...
One art/there is/no less/no more/All things/to do/with sparks/galore
--Douglas Hofstadter

Billy Tanksley — 2001-07-02 19:25:45

From: Manfred von Thun [mailto:phimvt@...]
>2. It is inelegant and unsafe. Ideally the strings "true" and "false"
> (and the half dozen others) should occur only once in the entire
> implementation.

You're expecting map to treat 'true' and 'false' as literals, not functions.
There's nothing wrong with that; however, why should map (and a few select
other functions) be the only ones?

Change your parser; remove 'true' and 'false' from the dictionary, and make
your lexer recognise them. 'false' should behave EXACTLY the same as '0'
(or whatever it's equivalent to) in ALL cases.

One other change would help: a change in your thinking. Define the meaning
of a reference to a function, and document the behavior of your built-in
functions with respect to that. 'true' is a special case (you had defined
it as a function when you actually wanted a literal), but there are other
times when you actually WANT a function.

In other words, your documentation should at least mention what gets left on
the stack when you type "[dup] first".

> - Manfred

-Billy

John Cowan — 2001-07-03 02:55:40

Billy Tanksley scripsit:

> Change your parser; remove 'true' and 'false' from the dictionary, and make
> your lexer recognise them.

I agree.

> 'false' should behave EXACTLY the same as '0'
> (or whatever it's equivalent to) in ALL cases.

It is not equivalent to anything, since Joy has explicitly typed values.
0 is not 0.0 is not false is not '\0.

> One other change would help: a change in your thinking. Define the meaning
> of a reference to a function, and document the behavior of your built-in
> functions with respect to that. 'true' is a special case (you had defined
> it as a function when you actually wanted a literal), but there are other
> times when you actually WANT a function.

It would be easy to add two functions "truth" and "falsity" which return
true and false respectively. These can be written directly in Joy
and put into the standard library.

What I'm not sure about is the proper treatment of "maxint" and "setsize"
and the others. They are constants in a particular implementation.

--
John Cowan cowan@...
One art/there is/no less/no more/All things/to do/with sparks/galore
--Douglas Hofstadter

Manfred von Thun — 2001-07-03 03:13:28

On Mon, 2 Jul 2001, John Cowan wrote:

[in reply to my thoughts on constants and functions]

> Yes, on reflection I agree with this: there should be no built-in constant
> functions in Joy, only constants with various spellings.
> So 5 and true should be on the same level.
> This will take some work to get right.

Ermph, yes, some work indeed. Here are my thoughts:

1 The internal code that is generated is OK in format, leave it.
2 Detailed differences between "dup" and "true" ONLY in raw Joy,
the operator table in file interp.c
3 The symbol table handles a) raw Joy and b) user defined stuff,
it has to be consulted when generating code in readfactor.
4 So, the information in the operator table must get to the
symbol table either
a) by copying during initialisation, or
b) by lookup throughout a session
5 Currently method 4a is used.
6 If 4a is continued and more (and more) info is put into the
operator table, then more (and more) needs to be copied to
the symbol table (during initialisation).
7 But the symbol table should not get bigger.
8 So, method 4b should be used.
9 For fast lookup during code generation the symbol table contains
(a pointer to) the strings "dup" "true" etc.
10 Code generated contains
a) for raw inbuilts, a pointer to a C-function
b) for user declared functions, a pointer to Joy code
11 For 10a the symbol table must access the C-function from the
operator table.
12 During initialisation of the symbol table currently
two kinds of items are copied to the symbol table:
a) (pointers to the) strings "dup" "true"
b) the C-code pointer
13 Instead of 12, to access any information in the operator
table during code generation, the following should be
put into the symbol table during initialisation:
a) as above
b) a pointer to the relevant record of the operator table.
14 Change 13 affects code generation in readfactor:
one extra indirection is needed.

To do all this, I recommend three stages A B C:

A No change in functionality yet, just implement 13 and 14:
A1. in globals.h a tiny change to declaration of symbol table fields
A2. in interp.c a tiny change to the loop in inisymtab()
A3. in utils.c a tiny change in readfactor for code generation
Now compile and run with just a few tests.

B Still no change in functionality:
B1. in interp.c, a tiny change to declaration of operator table
fields: at the end add two new fields "typefield"(8bit)
and "valuefield"(32bit).
B2. still in interp.c, a BIG change to the declaration the
operator table details:
an editor LOOP to add two dummy values 0 and 0 as values
in the new fields.
(in other words, replace every "}, by ",0,0},
and don't forget the last: replace "} by ",0,0} )
B3 for the 7 or 8 troublesome constants, change the dummies
to the real things, so that the operator fields (without
the help strings) now look like this:
name function typefield value field
"dup" dup_ 0 0 (most common)
"true" dummy_ BOOLEAN_ 1
"false" dummy_ BOOLEAN_ 0
"setsize" dummy_ INTEGER_ SETSIZE
B4. If you were to compile file interp.c now, you should get
a warning that the functions declared by calls to the PUSH macro
(true_,false_,setsize_ etc) are not called at all.
Maybe comment them out at this stage.
Compile, but there is no need to run.

C a tiny final change to achieve the new functionality:
C1. in file utils.c function readfactor when generating code,
if (typefield == 0) generate code as before
else generate special code in the form
typefield valuefield next
e.g. BOOLEAN_ 1 next
INTEGER_ 32 next
Compile, test the newly implemented constants outside and inside
lists, and SMILE.

This is as far as my thoughts about it have taken me.

I was supposed to go on a week long holiday, but I will only
know by Thursday late evening, and then I will leave early
Friday without being able to say that I cannot do the above
changes until about two weeks later. So, I'll not do the
changes in the next two days anyhow. If anybody wants to do them ...

- Manfred

BTW Philosophers do not use trig very much, but from what I remember
(from almost 1/2 C ago), sin(30.0) = 0.5 tan(45.0) = 1.0 (No ?)

Manfred von Thun — 2001-07-03 03:39:54

On Mon, 2 Jul 2001, Billy Tanksley wrote:

> From: Manfred von Thun [mailto:phimvt@...]
> >2. It is inelegant and unsafe. Ideally the strings "true" and "false"
> > (and the half dozen others) should occur only once in the entire
> > implementation.
>
> You're expecting map to treat 'true' and 'false' as literals, not functions.
> There's nothing wrong with that; however, why should map (and a few select
> other functions) be the only ones?
>
> Change your parser; remove 'true' and 'false' from the dictionary, and make
> your lexer recognise them.
That is what the quick fix does.

> 'false' should behave EXACTLY the same as '0'
> (or whatever it's equivalent to) in ALL cases.
except when writing:
true false and.
should produce
false

> One other change would help: a change in your thinking. Define the meaning
> of a reference to a function, and document the behavior of your built-in
> functions with respect to that. 'true' is a special case (you had defined
> it as a function when you actually wanted a literal), but there are other
> times when you actually WANT a function.
>
> In other words, your documentation should at least mention what gets left on
> the stack when you type "[dup] first".
My wording somewhere was unclear, sorry. Which particular documentation
were you thinking about?

- Manfred

Manfred von Thun — 2001-07-03 03:47:42

On Mon, 2 Jul 2001, John Cowan wrote:

> Billy Tanksley scripsit:
>
> > Change your parser; remove 'true' and 'false' from the dictionary, and make
> > your lexer recognise them.
>
> I agree.
Except: true and false deserve help strings.
(And in the suggestions about improved version of the changes which I
posted just now, the (now modified) dictionary (=optable) entries
are needed.

> > 'false' should behave EXACTLY the same as '0'
> > (or whatever it's equivalent to) in ALL cases.
>
> It is not equivalent to anything, since Joy has explicitly typed values.
> 0 is not 0.0 is not false is not '\0.
>
> > One other change would help: a change in your thinking. Define the meaning
> > of a reference to a function, and document the behavior of your built-in
> > functions with respect to that. 'true' is a special case (you had defined
> > it as a function when you actually wanted a literal), but there are other
> > times when you actually WANT a function.
>
> It would be easy to add two functions "truth" and "falsity" which return
> true and false respectively. These can be written directly in Joy
> and put into the standard library.
OK, will do.

> What I'm not sure about is the proper treatment of "maxint" and "setsize"
> and the others. They are constants in a particular implementation.
I am suggesting that they be treated as implementation dependent
constants, unlike "true" and "false".

- Manfred