I am idly exploring PicoLisp, and find myself perplexed about how to write meta-programming functions that would traditionally be handled with macros (in other lisp dialects). The biggest source of concern for me is that I do not see how I can prevent variable name shadowing. Reviewing the examples in Metaprogramming 101 has, if anything, just left me more confused.
Examples on how to implement the function mapeach
, as seen in the linked article:
[de mapeach "Args" # expression
(let [(@Var @List . @Body) "Args"]
(macro
(mapcar
'((@Var) . @Body)
@List ]
(de mapeach "Args"
(mapcar
(cons (cons (car "Args")) (cddr "Args"))
(eval (cadr "Args")) ) )
(de mapeach "Args"
(mapcar
'(("E")
(bind (car "Args")
(set (car "Args") "E")
(run (cddr "Args")) ) )
(eval (cadr "Args")) ) )
(de mapeach "Args"
(let "Vars" (pop '"Args")
(apply mapcar
(mapcar eval (cut (length "Vars") '"Args"))
(cons "Vars" "Args") ) ) )
I have tested each of these with the call (let "Args" * (mapeach N (1 2 3) ("Args" N N)))
. As expected, the PicoLisp interpreter (started with the command pil +
) experiences a segfault and crashes. I assume this is because mapeach
's "Args"
shadows the "Args"
defined at call point.
I also tried both of their implementations of map@
(the "cuter" alternative to mapeach
).
(de map@ "Args"
(mapcar
'(("E") (and "E" (run (cdr "Args")))) # 'and' sets '@'
(eval (car "Args")) ) )
(de map@ "Args"
(mapcar
'((@) (run (cdr "Args")))
(eval (car "Args")) ) )
I used (let "Args" * (map@ (1 2 3) ("Args" @ @)))
to test each of those implementations. Bizarrely, the first time I tested the first implementation, not only did it not segfault, it actually produced the correct result (1 4 9)
. Each subsequent test has resulted in a segfault. For clarity, the snippet from the prompt:
: (de map@ "Args"
(mapcar
'(("E") (and "E" (run (cdr "Args")))) # 'and' sets '@'
(eval (car "Args")) ) )
-> map@
: (let "Args" * (mapeach N (1 2 3) ("Args" N N)))
!? (mapeach N (1 2 3) ("Args" N N))
mapeach -- Undefined
?
: (let "Args" * (map@ (1 2 3) ("Args" @ @)))
-> (1 4 9)
I believe that the segfault was somehow prevented by the call to the (then) undefined function mapeach
, I also tried (ooga booga)
, which similarly prevented the segfault. If I do not have the erroneous call separating the definition from the proper call, a segfault always occurs.
This ultimately culminates in 2 questions:
According to this "The index for transient symbols is cleared automatically before and after loading a source file, or it can be reset explicitly with the ==== function". It doesn't specify any way that it is automatically cleared during regular REPL usage, which is the context in which I was testing this.
This code runs properly:
[de mapeach "Args" # expression
(let [(@Var @List . @Body) "Args"]
(macro
(mapcar
'((@Var) . @Body)
@List ]
(====)
(let "Args" * (mapeach N (1 2 3) ("Args" N N)))
It also runs as expected without the call to ====
, but only if the call to mapeach
is not in the same file.
To address the 2 parts of my question:
====
.