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