Re: picolisp reader dot handling inconsistency

2009-10-09 Thread Alexander Burger
Hi Tomas,

 I don't think so.  I have simple rules for the reader:
 - dot is a symbol as any other
 - if read inside a list, it means: place the next sexp into the current
   cells' cdr
 ...
 Why error?  Understanding of the three simple rules above is enough and
 there are not any surprises and pathological cases.

There are. You gave (1 2 3 . 4 . 5) as an example.

Your algorithm first replaces the cdr of the last cell of (1 2 3) with
'4', and then with '5'. So . 4 is discarded.

You violated your own rule, in that the next sexp is placed into the
current cell's cdr, because precisely that sexp (the '4') gets lost.

A naive user seeing the above expression would rather assume the first
dot to be a normal symbol (dot is a symbol as any other), and the
second dot to be the markup of a dotted pair. But implementing this
would require a read-ahead to find the last dot in an expression.

Or he would assume that the first dot is indeed a markup character, and
(4 . 5) is the next sexp, but this is also not correct because then
the expression should be written as (1 2 3 . (4 . 5)).

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


Re: picolisp reader dot handling inconsistency

2009-10-09 Thread Tomas Hlavaty
Hi Alex,

 I don't think so.  I have simple rules for the reader:
 - dot is a symbol as any other
 - if read inside a list, it means: place the next sexp into the current
   cells' cdr
 ...
 Why error?  Understanding of the three simple rules above is enough and
 there are not any surprises and pathological cases.

 There are. You gave (1 2 3 . 4 . 5) as an example.

 Your algorithm first replaces the cdr of the last cell of (1 2 3) with
 '4', and then with '5'. So . 4 is discarded.

 You violated your own rule, in that the next sexp is placed into the
 current cell's cdr, because precisely that sexp (the '4') gets lost.

no, the sexp '4' gets lost as a consequence of the rules not because I
violated the rules (simply because the last cell before reading 4 is the
same as before reading 5).  That rule does not cons any cell to the end.
Any other lisp reader is more constrained in these corner/error cases
and/or appears to have some additional arbitrary rules which I think are
not necessary.

 A naive user seeing the above expression would rather assume the first
 dot to be a normal symbol (dot is a symbol as any other), and the
 second dot to be the markup of a dotted pair. But implementing this
 would require a read-ahead to find the last dot in an expression.  Or
 he would assume that the first dot is indeed a markup character, and
 (4 . 5) is the next sexp, but this is also not correct because then
 the expression should be written as (1 2 3 . (4 . 5)).

Well, I think that the only thing a user of any other lisp could expect
in such cases is an error (in some other cases, a hard to guess
behaviour).  What I have done basically, is that I replaced weird errors
and arbitrary behaviour by simpler and more general algorithm.

 However, you get into another dilemma: Despite the fact that the dot
 is a legal internal symbol, you can never read it in a list, not even
 by escaping it. It is a kind of meta-symbol, solely for the reader.

Your are right that my reader cannot read the dot symbol inside a list.
The question is whether it is a problem or not.  I would argue that it
isn't a problem, people never (or hardly ever) use/type such things.

At the moment, I don't do escaping in the reader (except inside strings)
because everything is allowed except the few special characters: ()
Even ' and ` are not special and can be part of a symbol name.  Except
of course at the begining of a sexp (after 'skip').  The quoestion is
wherher escaping at the symbol level is useful and needed.  I personaly
never use it.  And, the user has always an option to intern a string.
It might get tricky with the dot inside a list but it is possible;

: '(1 2 3 . `(cons (intern .)) 4 . 5)
- (1 2 3 . 4 . 5)

would be needed to get the dot as a symbol before 4.  So there is a way
to achieve that, just not so convenient for cases that (maybe?) never
arise.

 Hmm, not sure. The current reader is (when you ignore special cases like
 NIL, read-macros, superparens etc.) in pseudo code:

(de read ()
   (ifn (= ( NextToken)
  (readAtom)
  (make
 (loop
(T (= ) NextToken)
   (skip) )
(T (=  .  NextToken)
   (chain (read)) )
(link (read)) ) ) ) )

 So the rule is that the dot is only treated as a meta-character when

- appearing in a list
- unescaped
- and not as part of a symbol

I don't think you stick to the rules though:-) See the examples I sent
before.  You'd have to add a few more rules like:

: '(. 1 2)
- (\. 1 2)

: '(. . 1)
- (\. . 1)

- not the first thing of the list

: '(. . .)
- (\. . \.)

- if last thing of the list, make the list circular

- unless the dot (meta-character) was before which won't make it
  circular but will treat is as a symbol

Too many rules IMHO.

I just noticed you added escaping of the dot symbol to the printer in
the latest version of picolisp;-)

Thank you,

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


Re: Concurrency

2009-10-09 Thread Tomas Hlavaty
Hi Nik,

 I didn't find anything useful about multithreading and
 concurrency model in PicoLisp by a quick search in google.

 Does PicoLisp has some sort of built-in concurrency at all?

picolisp doesn't do threads but uses processes and ipc instead (see
'fork', 'pipe', 'tell', 'hear', 'rpc' etc. in the picolisp reference).
For example, the database stuff or the web server uses multiple
processes.

There have been discussions about concurency on this mailing list in the
past I think.  When I tried to understand concurency in picolisp, I
wrote http://logand.com/sw/phil.l which implements the Dining
Philosophers Problem.

Is there anything specific you want to achieve?

Regards,

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


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: Apply functions with evaluating args vs non-evaluating args vs

2009-10-09 Thread Tomas Hlavaty
Hi Alex,

Only functions which evaluate all of their arguments should be passed
to 'apply'. For other functions the result is undefined.

I still wonder whether this restriction is necessary?

 'apply' is a bit tricky internally.

I will need to understand 'apply' first I guess;-)

- If it is a Lisp function with a single symbolic parameter
  (non-evaluating), that parameter is simply bound to NIL (as you
  observed).

Why cannot it be bound to the argument list being applied similar to the
evaluating case but without evaluation?

 : (and 1 2 3)
 - 3
 : (apply and (1 2) 3)
 - 2

 Note, here too, that 'and' is not really suited for 'apply', as it does
 not always evaluate all of its arguments. But, ok for now ...

Well, I don't see anything suspicious here, it behaves as I expect.

: (apply and (1 2) 3)
- 2
: (apply list (1 2) 3)
- (3 1 2)

So it properly returns 2 in the second case.

: (setq a NIL)
: (apply and (list a (print 1) (print 3)))
13- NIL
: (apply and (list a (print 1)) (print 3))
13- NIL

I see here that 'apply' evaluates the arguments before 'and'.  So maybe
here is where it gets evaluated twice as you mentioned it in other
discussion?

What if 'apply' did not evaluate the arguments and just passed the
argument list to the function being applied?  The native functions would
work same as now and when invoking a lisp function, there would be that
piece of code that prepares/binds the argument list (and evaluated or
not the arguments depending of the kind of argument atom/list).  This
way there would not be a difference between native and lisp functions
and both would behave the same even if applied.

(de and L
   (loop
  (NIL (eval (pop 'L) 1))
  (up @ @)
  (NIL (pair L) @) ) )

Thank you, this is what I meant:-)

 'apply' of course does not work, for the reasons explained above.

As applying native and lisp 'and' behaves differently so there seems to
be limit in what can be implemented in picolisp.

 What is the fundamental difference between
 
(de lfn L ...)
 
 and
 
any cfn(any ex) {...}

 Probably the fact that Lisp functions do not receive the full runtime
 expression, but only the arguments.

Yes.  But the full runtime expression is just convenience for error
handling/messages?

 The C-function 'and' recieves the list

(and (+ 3 4) (+ @ 1))

 while the Lisp function only recieves

((+ 3 4) (+ @ 1))

 Therefore, a Lisp function cannot access the function symbol (here
 'and').

Yes.

But there is something else which makes native functions special:-)

Thank you,

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


Re: Concurrency

2009-10-09 Thread Doug Snead
--- On Fri, 10/9/09, Tomas Hlavaty t...@logand.com wrote:=0A How many pro=
cesses do you need?=A0 I can fork about 500=0A from one parent=0A (limite=
d by my OS set up).=A0 And because picolisp is,=0A well, pico, it=0A do=
esn't take much memory (picolisp uses less that 2MB).=0A=0AAnd if picolisp =
is built as a shared object/dll, then additional picolisp processes may con=
sume even less memory. =0A=0ADoug=0A=0A=0A=0A  
-- 
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