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.000000000 -0300
+++ subr.l      2009-10-09 21:49:17.000000000 -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

Reply via email to