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). EVALUATION 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 through. 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... Examples: (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 function! 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) if: 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 others 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 -> "false" 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 list. 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