On 2018-07-13 07:19, Alexander Burger wrote:
First off, I'm confused about what the correct way of doing local
There's no "return" or "return-from" -- instead the closest thing I've
is "quit", which is sort of akin to "error" in CL
Correct. There is no 'return' function jumping directly out of a nested
structure. There are, however, 'catch' and 'throw', which implement the
mechanism (cleaning up all necessary stuff before doing the exit) -
syntactically as neat as a simple 'return'.
I use NIL as the catch tag in such cases, e.g.
(de foo (Args)
(let (A (bar) B (mumble))
(for I A
(when (lt0 (+ I B))
(throw) ) ) ) ) ) ) )
Here (throw) corresponds to a (return).
To return a value like (return 7) do (throw NIL 7).
The reason for not having a 'return' function is that it has to do a
cleanup till it reaches the exit (unbind variables, close files and
dynamic environments). This is initialized and set up by 'catch'.
When a function starts to run, the interpreter does not know yet that
might be a call to 'return' somewhere deeply nested inside. So it would
setup the catch environment for *every* function. This would be too
as most functions will never call 'return'.
Instead, it is done the PicoLisp way: Let the programmer be in control!
insert '(catch NIL' at the beginning of a function (or at any other
If you insist, you can create your own return function:
(de return (Val)
(throw NIL Val) )
The '(catch NIL' is still needed though.
'quit' is similar to 'throw'. It throws an error message instead of a
is triggered internally also when an error occurs, which then can be
'(catch '("Message 1" "Message 2") ...
I see, that makes a lot of sense. I generally expect stuff that is being
thrown to include a hefty payload of stack traces, environments and
other assorted guff. This approach is what I hoped for. :)
Then there's the conditional exits in "for", "do" and "loop" which
a real problem if you wish to terminate the loop from within a
(for X (1 2 3 4 5)
(let Y (mumble-mumble X)
(NIL Y (println "this doesn't work"))))
Yes. As also pointed out by Bruno, the 'T' and 'NIL' exit clauses must
be on the
top level of the body. Usually code can be rearranged this way. If not,
catch/throw can be used here too.
I'm also a pathological meta-programmer, and the lack of macros
bother me as much as I thought it would. However, one thing I miss
other lisps is a way of expanding macros. How would I go on about
For one thing, there are read-macros which are expanded at read-time.
Then, there is - also pointed out by Bruno - 'fill' and 'macro' which
be used to build and execute expressions at runtime. 'macro' is rather
used, as it introduces quite some overhead in an interpreted system.
The way to go for meta-programming is to use FEXPRs. Examples are many
PicoLisp package itself, or at
Oh, I know of all these things. (Though I don't have an appreciation for
how heavy "macro" is.) My problem -- I guess -- is that I have a
thoroughly ingrained idea of how meta-programming is supposed to behave
in a lisp. For instance, expanding macros makes sense in a language that
makes a distinction between macros and functions, but a language that
support FEXPRs I guess it's not that clear. From what I gather, I need
to shift mental gears here and not try to push a square peg into a round
Though, Bruno's suggestion of using "fill" is a great idea for
"macro-expanding" as one is coding along.
Thanks for the replies! :)