Hi dean

No worries, no need to be sorry, we like to help :-)
Here a more lengthy and maybe easier explanation, though not simpler...
Feedback appreciated.

>From which languages are you coming from?
It looks to me like you view lisp brackets ( ) similar to curly brackets { } in 
C/C++/C#/Java/..., where { }  is doing scoping.
While they appear similar at first, they don't really have much in common. 
Brackets in lisp are also used for grouping and nesting, but on a much more 
basic level.

Lets leave brackets aside first and have a look at the basic types of picolisp. 
When you grokked this, you will grok brackets.
Picolisp has three strongly typed types:

1) Number - represent a signed integral (integer) value of arbitrary size
In text form (as in source code) they are just plain written as numbers: 123
Whenever there is a number within picolisp source code (without any "quotes" 
around it), its automatically detected as a numeric integer value.

2) Symbol - has a value, and optionally a name and an arbitrary number of 
properties (also might have none)
If you're new to lisp, coming from a C-style language, then this is probably 
the weirdest and most complex data type to understand.
At minimum a symbol has a value, but the value can be anything - maybe it helps 
to think of it as a reference/pointer to some other value, e.g. a number, 
another symbol, or a list.
Most times, a symbol has also a name - think of it like the name of a variable 
in other languages.
Additionally a symbol can have none, one or multiple properties - which again 
consists of a name and a value.
(This makes symbols looking a bit like OOP classes in other languages, but the 
symbol type alone is not really a class or object, though the picolisp OOP 
classes are based on symbols).

Symbol is a very powerful and wildly diversely used type.
And there are actually kind of 4 different (sub-)types of symbols, which differ 
a bit in behaviour and usage context, but the description above is true for all 
of them.

Whenever there is a string within picolisp source code (without any "quotes" 
around it), its automatically detected as a symbol.
strings with "quotes" around it are actually also symbols, in picolisp we call 
them "transient symbols" because they live in a transient scope, what this 
means exactly you can look up later on, for the moment think of them as a 
symbol used to represent a character string.

ATOMS: The Number and Symbol types are called atoms, because they don't consist 
of other types.
Check out the function (atom): http://software-lab.de/doc/refA.html#atom

So, now we have 2 of the 3 picolisp base data types. Now lets look at the third 
data type, the one which is not an atom.

3) List - a sequence of one or more cells (cons pairs), holding numbers, 
symbols, or cons pairs.
Simply said, a list is a grouping of values, wherein each value can be a 
number, a symbol, or also another list.

In the picolisp source code, a list is surrounded by ( ) brackets (lisp users 
usually call them parens, not brackets, I guess this is  short for parenthesis).
Technically every ( ) in the picolisp source code denotes a list, but 
attention: not every ( ) in the code results in a list variable. Just keep 
carry on, you will understand the meaning of this when you finished the next 
section about evaluation below...

Two parens in source code, which only contain one or multiple Numbers, is 
automatically detected as a list value: (1 2 3 55)
A list value which contains other stuff, needs to be prefixed with ' (this is 
the quote-macro of picolisp): (symbol 2 3 this is a list) <- yep, this is a 
list containing the Numbers 2 and 3, and the Symbols "symbol", "this", "is", 
"a" and "list"

This are the three basic types of picolisp. Everything else in picolisp is made 
up from those types! Everything!
(and those are made up of cells, a cell is basically a single piece of memory 
in picolisp runtime).

This is the other fundamental topic to understand, evaluation, turning input 
into output, the essence of software.
Evaluation is a big topic in other programming languages too, but maybe the 
exact process is a bit more hidden and implicit than in lisp languages.
REPL stands for READ - EVALUATE - PRINT -LOOP, and this is exactly what the 
picolisp runtime does.
1) read the source code -> build a structure in memory which is an exact binary 
representation of the textual representation of the program, which is the 
source code.
2) evaluate -> turning input into output according to a certain set of rules
3) print -> print the result on the screen (or maybe not, for example: a pure 
server program talking over a network connection to a client program)
4) loop -> repeat again with step 1), until the program is exited by calling 
(bye) or is shut down by the OS

When you start picolisp without any arguments (invoking pil or pil +), you end 
up in the REPL.
When you enter something and press return, the steps of the REPL are followed 

Values of type Number are always evaluated as themselves, meaning 123 -> 
returns 123.
Symbols return their value, or return the special Symbol NIL if they don't have 
a value:
: A -> NIL
: (setq A 123)
:  A -> 123

Transient symbols, the special case of symbols with "quotes" around them, 
return their name, unless they have a value:
: "Switzerland" -> "Switzerland"
: (setq "Switzerland" "Schweiz") # set value of Symbol "Switzerland" to the 
german word
: "Switzerland" -> "Schweiz"

And now comes the magic...

Lists in source code, so everything with parens (around it), are evaluated as 
function calls!
Because functions are represented as list data type...

(print "Schwitzerland") -> this means: call the function print and give it, as 
an argument, whatever "Switzerland" evaluates to (so this could be 
"Switzerland" or "Schweiz", see the evaluation of transient symbols as 
explained just above)
(if (= 1 1) "true" "false") -> this means: call the function if and give it, as 
arguments, this other elements contained in this list

Normally, all arguments to a function are evaluated before executing the 
Some functions are special (so called fexpr, short for f-expressions, 
historically for "user defined special form")
The function if behaves like a fexpr (albeit to be exact, its not user defined 
but hardcoded in picolisp runtime)

evaluate the first argument,
if its non-NIL, evaluate and return the second argument,
otherwise (if the second argument evaluated to NIL) evaluate all other 
arguments (except the second argument) and return the result of the evaluation 
of the last argument

Step by step:
1) (if (= 1 2) "true" (prinl "bla") "false") -> oh we should evaluate the first 
argument, to find out what we need to do
2) (if NIL "true" (prinl "bla") "false") -> well, (= 1 2) evaluates to the 
Symbol NIL, so lets skip the evaluation of the second argument and evaluate all 
3) (prinl "bla") -> oh, a nested evaluation, ok lets evaluate the transient 
Symbol "bla", which results in "bla", and execute prinl on that value
4) ok, back in the if, what next? -> evaluate "false" -> easy, result is 
"false" -> ok, this was the last argument to if, lets return this value -> 

Lets see how a function is defined/created:
(de hello-world ()
   (prinl "hi world") )

Ha, this is again a list in the source code! Containing the symbol "de", the 
symbol "hello-world", an empty list (empty lists evaluate to NIL), and another 
How is this evaluated? As a call to the built-in function "de", which is also 
kinda a fexpr, taking all its argument as input without evaluation.
"de" then creates a new symbol with the name "hello-world" and the value as a 
list which looks like this: (() (prinl "hi world")) -> so the value is a list 
which contains an empty list plus a list with two symbols in it

Reply via email to