Re: onOff question
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
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
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
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
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
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
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
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
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
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