A New Realization about Common LISP

In This Posting, I explored the potential fact in great depth, that certain implementations of LISP may have Symbols, one field of which holds a value in the sense that a variable should hold a value, consisting of lists and atoms, while another field can hold a property list, also known as a “Symbol Plist”.

Well it has come to my attention, that Common LISP in particular, has some fixed constraints regarding this subject.

When Common LISP evaluates expressions that are lists, the CAR is allowed to be a nested list, which defines a lambda-expression. But otherwise, the CAR needs to be an atom – i.e., a symbol – which has a compiled function, stored in the plist.

We may export function-definitions into the value-field of a symbol, such as into a variable, like so:

 


(defvar *function-var*
    #'(lambda (pos-arg1 pos-arg2)
        (list pos-arg1 pos-arg2) ) )

 

If we do, then we must call or apply this function like so:

 



(funcall *function-var* 'Foo 'Blatz)

(apply *function-var* '(Foo Blatz) )


 

If we do not abide by this convention, we get error messages, because the function-definition in the value-field is not searched by Common LISP, when the symbol is itself the CAR of a list, and because when we assign lists as function-definitions, those do not get assigned to the symbol plist.

This observation seems to be corroborated here.

This convention, of Common LISP, accelerates execution greatly, because the interpreter does not need to make complicated arrangements, depending on what type of functions appear as the CAR of a list. The context now specifies that.

Once we are aware that this constraint exists, adapting our value-field-functions to it, is straightforward, because of the built-in function

(symbol-function 'symbol)

Which disembodies the part of the plist, that is supposed to hold functions and not values, in this case, belonging to 'symbol. Hence, the following experiment should succeed on a working Common LISP Compiler:

 



GCL (GNU Common Lisp)  2.6.12 CLtL1    Oct 28 2014 10:02:30
Source License: LGPL(gcl,gmp), GPL(unexec,bfd,xgcl)
Binary License:  GPL due to GPL'ed components: (XGCL READLINE UNEXEC)
Modifications of this banner must retain notice of a compatible license
Dedicated to the memory of W. Schelter

Use (help) to get some basic information on how to use GCL.
Temporary directory for compiler files set to /tmp/

>(setf (symbol-function 'my-function)
    (lambda (pos-arg1 pos-arg2)
        (list pos-arg1 pos-arg2) ) )

(LAMBDA-CLOSURE () () () (POS-ARG1 POS-ARG2) (LIST POS-ARG1 POS-ARG2))

>(my-function 'Foo 'Blatz)

(FOO BLATZ)

>(my-function 'Ergo 'Sum)

(ERGO SUM)

>(quit)


 

Now, the somewhat unexpected line output from the definition, is due to the (setf) function doing what all variable-setting functions in LISP do: They not only bind the value asked for, to the field, but also return the same value to the calling context, because doing so can make certain LISP code more practical.

It should be understood, that a function defined in this way will not be compiled, but rather interpreted, even though it has been assigned to the function-field of the symbol. Additionally, the following example will work:

 



GCL (GNU Common Lisp)  2.6.12 CLtL1    Oct 28 2014 10:02:30
Source License: LGPL(gcl,gmp), GPL(unexec,bfd,xgcl)
Binary License:  GPL due to GPL'ed components: (XGCL READLINE UNEXEC)
Modifications of this banner must retain notice of a compatible license
Dedicated to the memory of W. Schelter

Use (help) to get some basic information on how to use GCL.
Temporary directory for compiler files set to /tmp/

>(defvar *global-text*
    #'(lambda (pos-arg1 pos-arg2)
        (list pos-arg1 pos-arg2) ) )

*GLOBAL-TEXT*

>(setf (symbol-function 'text-to-function)
    *global-text*)

(LAMBDA-CLOSURE () () () (POS-ARG1 POS-ARG2) (LIST POS-ARG1 POS-ARG2))

>(text-to-function 'Foo 'Blatz)

(FOO BLATZ)

>(text-to-function 'Ergo 'Sum)

(ERGO SUM)

>(quit)

 

(Edit 09/27/2016 : Even though I named my symbols ‘text-to-function‘ above, this does not actually imply, that they will store any sort of text… They actually store a list. It can happen casually that, depending on context, a programmer will feel that a certain quantity of data is closer to being text, than certain other data is. And programmers may then refer to data as text, which is not actually text.

As my whole posting explains, when evaluating a LISP expression, lists need to be interpreted, while code which has been compiled into machine language, can just be executed by the CPU. Thus, when no explicit command has been given to compile lists, their treatment is closer to how certain other languages process text.)


 

There exists another subject in LISP, which I am going to describe here, which is called “closure”. It can happen that arguments are left dangling while evaluating an expression, that have not been bound to any parameters. Evaluating a (lambda) expression will gather those dangling arguments, into whatever parameters are provided within the (lambda) expression.

While this might just sound like sloppy coding, it can sometimes be useful. For example, there can be a defined function that accepts two arguments, but the first of those might be predefined, so that a derived function can exist, to which only the second argument (to the original function) needs to be supplied. Here is an example I adapted from the text I cited above:

 


GCL (GNU Common Lisp)  2.6.12 CLtL1    Oct 28 2014 10:02:30
Source License: LGPL(gcl,gmp), GPL(unexec,bfd,xgcl)
Binary License:  GPL due to GPL'ed components: (XGCL READLINE UNEXEC)
Modifications of this banner must retain notice of a compatible license
Dedicated to the memory of W. Schelter

Use (help) to get some basic information on how to use GCL.
Temporary directory for compiler files set to /tmp/

>(expt 2 4)

16

>(defun closure-fun (fun &rest values)
    (lambda (&rest more-values)
        (apply fun (append values more-values)) ) )

CLOSURE-FUN

>(closure-fun #'+ 3 5)

(LAMBDA-CLOSURE ((VALUES (3 5)) (FUN #)) ()
    ((CLOSURE-FUN BLOCK #<@0000000000E927E0>)) (&REST MORE-VALUES)
  (APPLY FUN (APPEND VALUES MORE-VALUES)))

>(apply (closure-fun #'+) (list 3 5))

8

>(funcall (closure-fun #'+ 3) 5)

8

>(setf (symbol-function 'power-of-3)
    (closure-fun #'expt 3) )

(LAMBDA-CLOSURE ((VALUES (3)) (FUN #)) ()
    ((CLOSURE-FUN BLOCK #<@0000000000E8B430>)) (&REST MORE-VALUES)
  (APPLY FUN (APPEND VALUES MORE-VALUES)))

>(power-of-3 3)

27

>(power-of-3 4)

81

>(quit)


 

The most immediate question I had to this example was,

‘I already used (defun) to define (closure-fun). Why, then, do I still need to use (funcall) to call that function?’

And my version of this example emphasizes the answer. (closure-fun) will run without any problems, but contains a (lambda) expression. Rarely before seen, what this other, (lambda) function does, is output a list, based on its environment. According to Common LISP, that list is itself not a function anymore. If we wish to evaluate that, we must specify a calling context, that evaluates plain lists as functions, therefore, the (funcall) invocation.

I have now built a function that can be called as-is, and which raises 3 to whatever power it is given.

Dirk

(Edit 09/27/2016 : ) I suppose that a more meaningful question to ask could be, ‘Why did the author not choose to prepend this (lambda) call with #' , like so:

 



(defun closure-fun (fun &rest values)
    #'(lambda (&rest more-values)
        (apply fun (append values more-values)) ) )


 

My short answer would be, that this (lambda) call occurs within the compiled function (closure-fun). By instinct, I will not put an interpreted function block inside a compiled function, unless there is no other way to obtain what I want.

The longer answer would be, that because (defun) always produces a function, by default it also compiles the function body, with an optimizing compiler. Some versions of LISP allow this to be switched off. Assuming this feature is enabled as by default, (defun) will also optimize its code, in a way that spans the call to (lambda). This makes (closure-fun) a kind of wrapper, for one specific call to (lambda). While the semantics should remain the same, the way I first wrote it should execute just a smidgen faster, than with the #' . If I had written a practical code example, this difference in speed would become significant.

If I was to use the #' here, this notation would mean, ‘Execute the (lambda) call, without any recognition of what the environment will be, and give me a list which represents what that is supposed to do.’ In the returned list, the atoms representing parameters, still need to get bound to eventual arguments. This tends to result in a more humanly-readable parameter list. But this self-contained format will also be slower to execute.

I would use that notation, in an example like this:

 



> (defvar *closure-ex1*
    (let ((var 10))
        #'(lambda () (setq var (+ var 1))) ) )

*CLOSURE-EX1*

> (funcall *closure-ex1*)

11

> (funcall *closure-ex1*)

12

> (funcall *closure-ex1*)

13

> (funcall *closure-ex1*)

14

> (funcall *closure-ex1*)

15

> (quit)


 

Firstly, this example does not propose a context for anything to be compiled, as a global variable is simply being set to a list. Secondly, the parameter ‘var‘ here can be bound to the numeric symbol (10) when the value of ‘*closure-ex1*’ is initialized. It remains a symbol afterward, which is not forgotten. The (let) is not called again, during the (funcall) invocations that follow. So there is no reason to doubt that this will work.

And so in the preceding example, I left it up to the compiler to resolve, and crossed my fingers. As it turned out, the compiler ended up putting (closure-fun), inside the (lambda-closure) expression it returned.

(Edit 10/01/2016 : ) If I am not mistaken, the insertion of the #' syntax, might also insert an additional evaluation of its parameter, which would have been correct when I was setting a global variable using (defvar), but which would not have been correct when using (defun) to define a function, the execution of which was supposed to evaluate (lambda) only once.

 

Print Friendly, PDF & Email

One thought on “A New Realization about Common LISP”

Leave a Reply

Your email address will not be published. Required fields are marked *

Please Prove You Are Not A Robot *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>