Explicit scope resolution in Pico Lisp

Today I felt like extending the html function that is responsible for rendering the bare bones of a HTML document in the GUI framework.

Normally a call to (html) looks something like:

(html 0 "Pico Admin" *CSS NIL
  (Some code here)
  (Some more code here) ... )

The important thing is the fact that the function accepts some arbitrary code(s) as the final argument. I wanted to simply hard code my stuff into an “extension”, like this:

(de pa-html Prg
  (html 0 "Pico Admin" *CSS NIL Prg))

Calling the above is shorter than the original with the same arguments over and over again and Prg is now the arbitrary code I referred to above.

It wouldn’t work though, what was going on? Well the answer is obvious when you get some help and think a little about it. In our (pa-html) above the Prg variable is a FEXPR; unevaluated code. With our addition we add a new scope, the local scope of (pa-html). It turns out that (html) doesn’t expect the code to come from this local scope, a lot of variables that are defined in the scope calling (pa-html) are probably undefined within the local scope of (pa-html) for instance. Hence the massive crashing.

To understand the solution, let’s start over from the beginning with the (up) function:

(let Hello "Hello"
  (println Hello)
  (let Hello "Hi!"
     (println Hello)
     (let Hello "Greetings"
        (println Hello)
        (println (up Hello)))))

Output:

"Hello"
"Hi!"
"Greetings"
"Hi!"

So the call to (up) will check what Hello contained in the scope above the scope that it was called in. In this case “Hi!” was the contents. If I say that the default value for (up) is 1 you can probably figure out what the last (println) would’ve printed had we passed it (up 2 Hello) instead.

Both (run) and (eval) can accept a last optional argument that will define their scope explicitly, let’s start with (run):

(de Lvl1 (Arg . Prg)
  (let Lvl "lvl: 1"
     (run Prg 1)))
  
(de Lvl2 ()
  (let Lvl "lvl: 2"
     (Lvl1 "some argument" (println Lvl) (println "Something more here"))))
  
(Lvl2)

Output:

"lvl: 2"
"Something more here"

As you can see (run Prg 1) will run Prg within the scope of (Lvl2), “one up”. I think you can guess what the output would be if we just did (run Prg).

By this point you should be able to understand that

(de pa-html Prg
  (html 0 "Pico Admin" *CSS NIL (run Prg 1)))

is how (pa-html) should look.

Let’s finish off with (eval) for good measure:

(de Lvl1 (Arg Lst)
  (let Lvl "lvl1"
     (eval Lst 1)))
  
(de Lvl2 ()
  (let Lvl "lvl2"
     (Lvl1 "I'm Arg" '(pack Arg " and I'm: " Lvl))))
  
(Lvl2)

Output:

" and I'm: lvl2"

Since Arg is undefined within the scope of (Lvl2) we won’t get “I’m Arg” in the beginning of the output. Just calling (eval Lst) would of course result in “I’m Arg and I’m: lvl1”.

I think it’s a bad idea to rely on explicit scope resolution out of laziness, as you can imagine the code could easily become unintelligible if not used sparingly. Use only when absolutely necessary if you can not see any other solution.

Related Posts

Tags: , ,