Re: onOff question

2009-10-10 Thread TC

On Sat, 10 Oct 2009, Alexander Burger wrote:

Hi TC,

thanks for your proposal.

However, as I explained in another mail, I would definitely not change
the current behavior of 'list' or 'cons' (and most other functions in
picoLisp).



I'm uncertain if '() is actually (NIL . NIL) so I'm not going to list
the cons' examples.


No, these are different things. () is converted by the reader as a
reference to the internal symbol 'NIL'. (NIL . NIL), which is the same
as (NIL), is a cell containing references to NIL in the CAR and CDR
parts.

(cons) is my standard way to create a fresh cell.




I know why this works this way. In pL's VM, every list is parsed until
there's an empty CDR (retreiving CDR returns NIL). Since there are

^ call this "atomic CDR"


functions that MUST take at least one argument; when they are called
with no argument, they will try to read NIL's CDR, and therefore use NIL
as arguments, since (cdr NIL) -> NIL


Correct.


:-D




Here are a couple of solutions for this issue, you pick:
  1. Make it error when (cdr NIL) (increases complexity and decreases 
flexibility).
  2. Make pL check on every step if the current argument is actually the last 
CAR (naive and maybe slow).
  3. Make pL check the amount of arguments provided and return NIL if they are 
not enough for the function to do it's job. (slightly increases complexity)
  4. Bury the topic.


A good roundup :-)

Points (1) and (2) would actually be rather stupid and useless, but can
be seen in some Lisp and Scheme implementations.


Right, that's why I listed them :-)


The original design of picoLisp put a lot of effort into the handling of
NIL. While NIL is in other Lisps just a symbol, it has a dual nature in
picoLisp (see "doc/structures" and "doc64/structures"):

 NIL:  /
   |
   V
 +-+-+-+-+
 |'LIN'|  /  |  /  |  /  |
 +-+--+--+-+-+

While it is a symbol physically (the pointer is at an multiple of the
cell size plus half a cell), accessing the pointer with CAR (offset
zero) and CDR (offset half a cell size) is legal and will always return
NIL again. This allows a very fast traversal of lists, without the need
for checking for end-of-list.

So (3) comes without any cost.


I'll dig this topic further :-)


Anyway, I just couldn't resist the temptation to try to implement
solution nº 3, so I grabbed the sources, segfaulted the hell out of it


Great! :-)


when I tried to avoid repeating code, and after a long while of ERTCL
(edit, recompile, test, cuss, loop), I decided to leave it like that for
now, and ask for a revision.


Well, first of all, a function that counts arguments before evaluating
them should be avoided like hell. This was the main reason for rewriting
picoLisp in assembly language (explained in "doc64/README"). And,
fortunately, it is also not necessary. Usually a simple

  atom (X CDR)

will tell you if there are any (or more) arguments.


Oh well, missed that, but I wanted to get the hang of pLAsm and this seemed to 
be a nice exercise to warm up so..


Besides this, what you wrote looks really good!


+### Internal function ###
+(code 'argLen 2)
+   push X
+   push Y
...
+  ld Y (X CDR)  # Next cell
+  cmp Y X  # Circular?
+  jz lengthT  # Yes
...
+  add E (hex "10")  # Increment counter
+   loop
+   ld Y X  # Keep list head<- This should be before the "Circular?" check?
+   ret


Seems it's still too early for me to do full fledged asm programming yet.


Re: onOff question

2009-10-10 Thread Alexander Burger
Hi TC,

thanks for your proposal.

However, as I explained in another mail, I would definitely not change
the current behavior of 'list' or 'cons' (and most other functions in
picoLisp).


> I'm uncertain if '() is actually (NIL . NIL) so I'm not going to list
> the cons' examples.

No, these are different things. () is converted by the reader as a
reference to the internal symbol 'NIL'. (NIL . NIL), which is the same
as (NIL), is a cell containing references to NIL in the CAR and CDR
parts.

(cons) is my standard way to create a fresh cell.



> I know why this works this way. In pL's VM, every list is parsed until
> there's an empty CDR (retreiving CDR returns NIL). Since there are
 ^ call this "atomic CDR"

> functions that MUST take at least one argument; when they are called
> with no argument, they will try to read NIL's CDR, and therefore use NIL
> as arguments, since (cdr NIL) -> NIL

Correct.


> Here are a couple of solutions for this issue, you pick:
>   1. Make it error when (cdr NIL) (increases complexity and decreases 
> flexibility).
>   2. Make pL check on every step if the current argument is actually the last 
> CAR (naive and maybe slow).
>   3. Make pL check the amount of arguments provided and return NIL if they 
> are not enough for the function to do it's job. (slightly increases 
> complexity)
>   4. Bury the topic.

A good roundup :-)

Points (1) and (2) would actually be rather stupid and useless, but can
be seen in some Lisp and Scheme implementations.


The original design of picoLisp put a lot of effort into the handling of
NIL. While NIL is in other Lisps just a symbol, it has a dual nature in
picoLisp (see "doc/structures" and "doc64/structures"):

  NIL:  /
|
V
  +-+-+-+-+
  |'LIN'|  /  |  /  |  /  |
  +-+--+--+-+-+

While it is a symbol physically (the pointer is at an multiple of the
cell size plus half a cell), accessing the pointer with CAR (offset
zero) and CDR (offset half a cell size) is legal and will always return
NIL again. This allows a very fast traversal of lists, without the need
for checking for end-of-list.

So (3) comes without any cost.



> Anyway, I just couldn't resist the temptation to try to implement
> solution nº 3, so I grabbed the sources, segfaulted the hell out of it

Great! :-)

> when I tried to avoid repeating code, and after a long while of ERTCL
> (edit, recompile, test, cuss, loop), I decided to leave it like that for
> now, and ask for a revision.

Well, first of all, a function that counts arguments before evaluating
them should be avoided like hell. This was the main reason for rewriting
picoLisp in assembly language (explained in "doc64/README"). And,
fortunately, it is also not necessary. Usually a simple

   atom (X CDR)

will tell you if there are any (or more) arguments.

Besides this, what you wrote looks really good!

> +### Internal function ###
> +(code 'argLen 2)
> +   push X
> +   push Y
> ...
> +  ld Y (X CDR)  # Next cell
> +  cmp Y X  # Circular?
> +  jz lengthT  # Yes
> ...
> +  add E (hex "10")  # Increment counter
> +   loop
> +   ld Y X  # Keep list head<- This should be before the "Circular?" 
> check?
> +   ret

Cheers,
- Alex
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe


Re: onOff question

2009-10-09 Thread Alexander Burger
Hi Tomas,

> > All functions ignore atomic CDRs of the last argument cell. You could
> > also try (onOff A B . X), the 'X' will be simply ignored.
> 
> so why is not NIL in the (onOff . NIL) ignored? ;-)

Well, the NIL _is_ ignored, in the same sense as the 'X' is ignored. The
NIL you observe results from the fact that missing arguments evaluate to
NIL.


> 'list' is a good example where I find this behaviour rather strange.
> 
> >: (list)
> >-> (NIL)
> 
> Here I would expect NIL and not (NIL).  Any other lisp I know works this

I would definitely always implement 'list' in this way. The reasons are
very pragmatic:

1. Why should anybody call (list) to get 'NIL'? If somebody wants 'NIL',
   he should write 'NIL'.

2. If you, however, want to create a single-cell list containing 'NIL'
   (for example, because you need an empty cell that is filled later),
   you have to write (list NIL) or (cons NIL NIL). But as the first
   argument evaluates to NIL anyway, you can save space in your program
   (a single cell each time) by writing (list) instead of (list NIL).

   I do this quite frequently with (cons), if I need a single cell.
   'cons' and 'list' behave identical in this situation.

3. If 'list' were implemented to return NIL if called without arguments,
   one additional runtime argument check is necessary, introducing
   overhead only for the pathological case where somebody wants to get
   'NIL' from (list) instead of simply writing 'NIL' directly.


> seems broken to me.  Do you have any example or code in picolisp that
> takes advantage of this behaviour?

Not for (list), but for (cons) there are plenty:

   lib/form.l:120
  (*PRG (pushForm (cons)))

   lib/simul.l:161
  (cons (cons) (cons))

   test/lib.l:36
  '((N) (later (cons) (cons *Pid (* N N

and I find more in production applications.

Cheers,
- Alex
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe


Re: onOff question

2009-10-09 Thread TC

On Fri, 9 Oct 2009, Tomas Hlavaty wrote:

Hi Alex,


All functions ignore atomic CDRs of the last argument cell. You could
also try (onOff A B . X), the 'X' will be simply ignored.


so why is not NIL in the (onOff . NIL) ignored? ;-)


zero arguments (as in 'onOff') are not to be expected, the function goes
straight on and "takes" the first argument, which happens to be NIL in
this case. So 'onOff' does not distinguish (nor even check) the
difference between

   (onOff)

and

   (onOff NIL)

There is no practical reason to do so, because calling 'onOff' without
arguments makes no sense. Remember, it is a non-evaluating function, and
cannot be called in a less predictable way (e.g. being passed through
'apply', mappings etc.).


Ok.


I think that most functions in PicoLisp behave like this. Try 'list',
for example:

   : (list NIL)
   -> (NIL)
   : (list)
   -> (NIL)

It does not care to check for the first argument.


'list' is a good example where I find this behaviour rather strange.


   : (list)
   -> (NIL)


Here I would expect NIL and not (NIL).  Any other lisp I know works this
way and it also makes sense.  I.e.  the command says make a list of
nothing (not even a NIL) and an empty list is NIL so this behaviour
seems broken to me.  Do you have any example or code in picolisp that
takes advantage of this behaviour?


Ugh, I also find that weird:

: '()# Empty list
-> NIL
: (list '())
-> (NIL)
: (list) # List nothing
-> (NIL) # I think this should be NIL

I'm uncertain if '() is actually  (NIL . NIL) so I'm not going to list the 
cons' examples.

I know why this works this way. In pL's VM, every list is parsed until there's an 
empty CDR (retreiving CDR returns NIL). Since there are functions that MUST take 
at least one argument; when they are called with no argument, they will try to 
read NIL's CDR, and therefore use NIL as arguments, since (cdr NIL) -> NIL

Here are a couple of solutions for this issue, you pick:
  1. Make it error when (cdr NIL) (increases complexity and decreases 
flexibility).
  2. Make pL check on every step if the current argument is actually the last 
CAR (naive and maybe slow).
  3. Make pL check the amount of arguments provided and return NIL if they are 
not enough for the function to do it's job. (slightly increases complexity)
  4. Bury the topic.

Regarding option 3, if there is a significant number of functions that would 
benefit from this, a compiler macro should be useful. This data could be listed 
together with the declaration header:
(code 'doList 2 1)  <-- This means: Needs at least 1 real argument so (list) -> 
NIL   and   (list NIL) -> (NIL)

Anyway, I just couldn't resist the temptation to try to implement solution nº 
3, so I grabbed the sources, segfaulted the hell out of it when I tried to 
avoid repeating code, and after a long while of ERTCL (edit, recompile, test, 
cuss, loop), I decided to leave it like that for now, and ask for a revision.

I'm attaching the working patch for the 'doList function.--- subr.l.~1~  2009-09-30 09:24:22.0 -0300
+++ subr.l  2009-10-09 21:49:17.0 -0300
@@ -883,11 +883,49 @@
pop X
ret
 
+### Internal function ###
+(code 'argLen 2)
+   push X
+   push Y
+   ld X E
+   ld E ONE  # Counter
+   do
+  cmp X Quote
+   while eq
+  ld Y (X CDR)  # Next cell
+  cmp Y X  # Circular?
+  jz lengthT  # Yes
+  ld X Y
+  atom X  # Done?
+  jnz 10  # Yes
+  add E (hex "10")  # Increment counter
+   loop
+   ld Y X  # Keep list head
+   do
+  ld X (X CDR)  # Next cell
+  atom X  # Any?
+   while z  # Yes
+  cmp X Y  # Hit head?
+  jz lengthT  # Yes
+  add E (hex "10")  # Increment counter
+   loop
+10 pop Y
+   pop X
+   ret
+
 # (list 'any ['any ..]) -> lst
 (code 'doList 2)
push X
push Y
ld X (E CDR)  # Args
+   call argLen # Args length
+   cmp E ONE # No args?
+   if eq  # Right
+  ld E Nil
+  pop Y
+  pop X
+  ret
+   end
ld E (X)  # Eval first
eval
call consE_C  # Cons with NIL


Re: onOff question

2009-10-09 Thread Tomas Hlavaty
Hi Alex,

> All functions ignore atomic CDRs of the last argument cell. You could
> also try (onOff A B . X), the 'X' will be simply ignored.

so why is not NIL in the (onOff . NIL) ignored? ;-)

> zero arguments (as in 'onOff') are not to be expected, the function goes
> straight on and "takes" the first argument, which happens to be NIL in
> this case. So 'onOff' does not distinguish (nor even check) the
> difference between
>
>(onOff)
>
> and
>
>(onOff NIL)
>
> There is no practical reason to do so, because calling 'onOff' without
> arguments makes no sense. Remember, it is a non-evaluating function, and
> cannot be called in a less predictable way (e.g. being passed through
> 'apply', mappings etc.).

Ok.

> I think that most functions in PicoLisp behave like this. Try 'list',
> for example:
>
>: (list NIL)
>-> (NIL)
>: (list)
>-> (NIL)
>
> It does not care to check for the first argument.

'list' is a good example where I find this behaviour rather strange.

>: (list)
>-> (NIL)

Here I would expect NIL and not (NIL).  Any other lisp I know works this
way and it also makes sense.  I.e.  the command says make a list of
nothing (not even a NIL) and an empty list is NIL so this behaviour
seems broken to me.  Do you have any example or code in picolisp that
takes advantage of this behaviour?

Thank you,

Tomas
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe


Re: onOff question

2009-09-22 Thread Alexander Burger
Hi Tomas,

> > Yes. But on the other hand, missing arguments usually default to NIL in
> > picoLisp. Makes not much sense in the case of 'onOff'.
> 
> I know, but it still doesn't make sense even in v2.3.7.

That's not what I mean, it has nothing to do with the version.


> : (onOff)
> 
> is the same as
> 
> : (onOff . NIL)
> 
> Now using this logic, 
> 
> : (onOff A B)
> 
> shoud be the same as:
> 
> : (onOff A B . NIL)
> 
> why is it trying to set NIL in the first case and not in the second
> one?;-)

All functions ignore atomic CDRs of the last argument cell. You could
also try (onOff A B . X), the 'X' will be simply ignored.


Functions are free in how they evaluate their arguments. In cases where
zero arguments (as in 'onOff') are not to be expected, the function goes
straight on and "takes" the first argument, which happens to be NIL in
this case. So 'onOff' does not distinguish (nor even check) the
difference between

   (onOff)

and

   (onOff NIL)

There is no practical reason to do so, because calling 'onOff' without
arguments makes no sense. Remember, it is a non-evaluating function, and
cannot be called in a less predictable way (e.g. being passed through
'apply', mappings etc.).

I think that most functions in PicoLisp behave like this. Try 'list',
for example:

   : (list NIL)
   -> (NIL)
   : (list)
   -> (NIL)

It does not care to check for the first argument.

Cheers,
- Alex
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe


Re: onOff question

2009-09-22 Thread Tomas Hlavaty
Hi Alex,

>> is this supposed to happen?
>> ..
>> : (onOff)
>> -> T
>> : (show NIL)
>> T T
>
> Oops, no. But if I try here:
>
> : (onOff)
> !? (onOff)
> NIL -- Protected symbol
> ? 
>
> I really don't remember, but it could well be that I "repaired" that
> in the course of rewriting some parts while codig the 64-bit version.

I see, my example was with v2.3.5, your is with v2.3.7.

>> (onOff sym ..) -> flg
>> 
>>Logical negates the VAL's of all argument symbols sym. Returns the
>>new value of the last symbol.
>> 
>> - Should not the symbol names be passed explicitly?
>
> Yes. But on the other hand, missing arguments usually default to NIL in
> picoLisp. Makes not much sense in the case of 'onOff'.

I know, but it still doesn't make sense even in v2.3.7.

: (onOff)

is the same as

: (onOff . NIL)

Now using this logic, 

: (onOff A B)

shoud be the same as:

: (onOff A B . NIL)

why is it trying to set NIL in the first case and not in the second
one?;-)

>> - Why does it return value of the last symbol?
> What would you return instead? Most similar functions like 'on', 'off',
> 'zero' or 'setq' do the same.

Well, I thought it might be more convenient not to specify return values
for some functions to allow for some optimizations but I guess it is not
an issue here.

Thank you,

Tomas
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe


Re: onOff question

2009-09-16 Thread Alexander Burger
Hi Tomas,

sorry, I missed the last two questions:

> (onOff sym ..) -> flg
> 
>Logical negates the VAL's of all argument symbols sym. Returns the
>new value of the last symbol.
> 
> - Should not the symbol names be passed explicitly?

Yes. But on the other hand, missing arguments usually default to NIL in
picoLisp. Makes not much sense in the case of 'onOff'.


> - Why does it return value of the last symbol?

What would you return instead? Most similar functions like 'on', 'off',
'zero' or 'setq' do the same.

Cheers,
- Alex
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe


Re: onOff question

2009-09-16 Thread Alexander Burger
Hi Tomas,

> is this supposed to happen?
> ..
> : (onOff)
> -> T
> : (show NIL)
> T T

Oops, no. But if I try here:

: (onOff)
!? (onOff)
NIL -- Protected symbol
? 

I really don't remember, but it could well be that I "repaired" that in
the course of rewriting some parts while codig the 64-bit version.

Cheers,
- Alex
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe


onOff question

2009-09-16 Thread Tomas Hlavaty
Hi Alex,

is this supposed to happen?

: (show NIL)
NIL NIL
-> NIL
: (onOff)
-> T
: (show NIL)
T T
-> T
: (=T NIL)
-> T
: 

(onOff sym ..) -> flg

   Logical negates the VAL's of all argument symbols sym. Returns the
   new value of the last symbol.

- Should not the symbol names be passed explicitly?
- Why does it return value of the last symbol?

Thanks,

Tomas
-- 
UNSUBSCRIBE: mailto:picol...@software-lab.de?subject=unsubscribe