From: ········@irtnog.org
Subject: What's the best style for using symbols in other packages, shadowing symbols, etc.?
Date: 
Message-ID: <w4o7kstnmcs.fsf@eco-fs1.irtnog.org>
My apologies in advance for the long lines.

I'm writing a compiler that must maintain dynamic state separate from
the Lisp run-time system (e.g. a symbol inside the compiler is not a
CL:SYMBOL object).  The compiler proper is contained in the
SYS.COMPILER package, while I've decided to put all of the compilation
environment code in a separate package called SYS.COMPILER.ENV.

Since SYS.COMPILER.ENV is going to end up shadowing a large portion of
the COMMON-LISP package, I've decided that importing CL into
SYS.COMPILER.ENV is a bad idea.  Hence, the following package
definition:

(defpackage "SYS.COMPILER.ENV"
  (:use nil)
  (:export "SYMBOL" "SYMBOL-PACKAGE" "SYMBOL-NAME" "SYMBOL-PLIST"
           "SYMBOL-VALUE" "SYMBOL-FUNCTION"))

(Of course, the export list is incomplete.)

When I write code for SYS.COMPILER.ENV, it looks like this:

(defparameter sys.compiler.env::*the-unbound-value* (gensym "THE-UNBOUND-VALUE-"))
(defparameter sys.compiler.env::*the-unbound-function* (gensym "THE-UNBOUND-FUNCTION-"))

(defclass sys.compiler.env:symbol ()
  ((sys.compiler.env::package :reader sys.compiler.env:symbol-package
                              :type (or null sys.compiler.env:package)
                              :initform nil)
   (sys.compiler.env::name :reader sys.compiler.env:symbol-name
                           :type string
                           :initarg name)
   (sys.compiler.env::plist :accessor sys.compiler.env:symbol-plist
                            :type list
                            :initform nil)
   (sys.compiler.env::value :accessor sys.compiler.env:symbol-value
                            :initform sys.compiler.env::*the-unbound-value*)
   (sys.compiler.env::function :accessor sys.compiler.env:symbol-function
                               :initform sys.compiler.env::*the-unbound-function*)))

(defun sys.compiler.env::copy-symbol (sys.compiler.env::symbol &optional sys.compiler.env::copy-properties)
  (declare (type sys.compiler.env:symbol sys.compiler.env::symbol))
  (let ((sys.compiler.env::new-symbol
         (make-instance 'symbol :name (sys.compiler.env:symbol-name sys.compiler.env::symbol))))
    (when sys.compiler.env::copy-properties
      (setf (sys.compiler.env:symbol-plist sys.compiler.env::new-symbol)
            (copy-list (sys.compiler.env:symbol-plist sys.compiler.env::symbol)))
      (setf (sys.compiler.env:symbol-value sys.compiler.env::new-symbol)
            (sys.compiler.env:symbol-value sys.compiler.env::symbol))
      (setf (sys.compiler.env:symbol-function sys.compiler.env::new-symbol)
            (sys.compiler.env:symbol-function sys.compiler.env::symbol)))))

And so forth.  As I write this all out, I realize I am still going to
have problems if I import SYS.COMPILER.ENV into SYS.COMPILER, which
uses the CL package.

So what's the best way to manage symbols and packages?  I don't want
to confuse myself too badly by playing games with the package system,
but I also want to keep the entire compiler package separate from the
Lisp run-time.

I'm such a Lisp neophyte that I could use just about any advice.

-- 
"We know for certain only when we know little.  With knowlege, doubt
increases." - Goethe

From: Thomas F. Burdick
Subject: Re: What's the best style for using symbols in other packages, shadowing symbols, etc.?
Date: 
Message-ID: <xcvwv0tow77.fsf@apocalypse.OCF.Berkeley.EDU>
········@irtnog.org writes:

[ Wow, it really *is* roll-your-own-Lisp-system season! ]

> My apologies in advance for the long lines.
> 
> I'm writing a compiler that must maintain dynamic state separate from
> the Lisp run-time system (e.g. a symbol inside the compiler is not a
> CL:SYMBOL object).  The compiler proper is contained in the
> SYS.COMPILER package, while I've decided to put all of the compilation
> environment code in a separate package called SYS.COMPILER.ENV.
> 
> Since SYS.COMPILER.ENV is going to end up shadowing a large portion of
> the COMMON-LISP package, I've decided that importing CL into
> SYS.COMPILER.ENV is a bad idea.  Hence, the following package
> definition:
> 
> (defpackage "SYS.COMPILER.ENV"
>   (:use nil)
>   (:export "SYMBOL" "SYMBOL-PACKAGE" "SYMBOL-NAME" "SYMBOL-PLIST"
>            "SYMBOL-VALUE" "SYMBOL-FUNCTION"))
> 
> (Of course, the export list is incomplete.)

I'm writing a Lisp compiler, too.  All of the symbols for my Lisp
dialect live in the CL80 package.  My defpackage forms look like:

  (defpackage :cl80
    (:use :cl)
    (:export "SYMBOL" "SYMBOL-PACKAGE" "SYMBOL-NAME" ...)
    (:shadow "SYMBOL" "SYMBOL-PACKAGE" "SYMBOL-NAME" ...))

  (defpackage :cl80-compiler
    (:use :cl)
    (:export "COMPILE-FORM" ...))

I don't really need to (:use :cl) for CL80, but it's a nice way of
getting all the symbols I need in there, while my (:export ...) and
(:shadow ...)  lines record what I've actually implemented :-).

I don't export the functions that contain the compiler's internal
state, so one can use the CL80-COMPILER package without getting things
like *LEXICAL-TAGS*, *FREE-REFERENCES*, etc, coming along for the
ride.

> When I write code for SYS.COMPILER.ENV, it looks like this:
> 
> (defparameter sys.compiler.env::*the-unbound-value* (gensym "THE-UNBOUND-VALUE-"))
> (defparameter sys.compiler.env::*the-unbound-function* (gensym "THE-UNBOUND-FUNCTION-"))

The only functions that care about variables in the SYS.COMPILER.ENV
package are in the SYS.COMPILER package, right?  So just make those
internal symbols in SYS.COMPILER.

> (defclass sys.compiler.env:symbol ()
>   ((sys.compiler.env::package :reader sys.compiler.env:symbol-package
>                               :type (or null sys.compiler.env:package)
>                               :initform nil)
>    (sys.compiler.env::name :reader sys.compiler.env:symbol-name
>                            :type string
>                            :initarg name)
>    (sys.compiler.env::plist :accessor sys.compiler.env:symbol-plist
>                             :type list
>                             :initform nil)
>    (sys.compiler.env::value :accessor sys.compiler.env:symbol-value
>                             :initform sys.compiler.env::*the-unbound-value*)
>    (sys.compiler.env::function :accessor sys.compiler.env:symbol-function
>                                :initform sys.compiler.env::*the-unbound-function*)))

You should do this in a file that starts with

  (defpackage :sys.compiler.env ...)
  (in-package :sys.compiler.env)

Then it would be:

  (defclass symbol ...)

By the way, for the package where the symbols for your lisp dialect
live, I'd recommend that you give it a short nickname, so when you're
in code that uses your package, you can do cl:lambda to refer to cl's
symbol, and when you're using the CL package (like in your compiler),
you can do my:lambda to refer to yours.

 [...]
> And so forth.  As I write this all out, I realize I am still going to
> have problems if I import SYS.COMPILER.ENV into SYS.COMPILER, which
> uses the CL package.
> 
> So what's the best way to manage symbols and packages?  I don't want
> to confuse myself too badly by playing games with the package system,
> but I also want to keep the entire compiler package separate from the
> Lisp run-time.

Here's what I'd recommend:

  1. Put all the symbols that comprise your dialect in one package, FOO.

  2. Write your compiler in FOO.COMPILER, which uses CL, but not FOO.
     From this package, you can refer to FOO:LAMBDA or LAMBDA (from CL).

  3. Put all of your symbols that you have in SYS.COMPILER.ENV in
     FOO.COMPILER, but don't export them.

  4. Code written in your dialect can do:

      (in-package :foo-user)
      (use-package :foo)
      (defun make-adder (x)
        #'(lambda (y) (+ x y)))

     What your compiler will see is:

      (foo:defun foo-user:make-adder (foo-user:x)
        (foo:function (foo:lambda (foo-user:y)
                        (foo:+ foo-user:x foo-user:y))))

-- 
           /|_     .-----------------------.                        
         ,'  .\  / | No to Imperialist war |                        
     ,--'    _,'   | Wage class war!       |                        
    /       /      `-----------------------'                        
   (   -.  |                               
   |     ) |                               
  (`-.  '--.)                              
   `. )----'                               
From: ········@irtnog.org
Subject: Re: What's the best style for using symbols in other packages, shadowing symbols, etc.?
Date: 
Message-ID: <w4osnbgn8o1.fsf@eco-fs1.irtnog.org>
>>>>> "Thomas" == Thomas F Burdick <···@apocalypse.OCF.Berkeley.EDU> writes:

    Thomas> I'm writing a Lisp compiler, too.  All of the symbols for
    Thomas> my Lisp dialect live in the CL80 package.
[...]
    Thomas> Here's what I'd recommend:

    Thomas>   1. Put all the symbols that comprise your dialect in one
    Thomas> package, FOO.

    Thomas>   2. Write your compiler in FOO.COMPILER, which uses CL,
    Thomas> but not FOO. From this package, you can refer to
    Thomas> FOO:LAMBDA or LAMBDA (from CL).

As I understand it, the compilation environment must be kept separate
from run-time environment regardless of whether the compiler is native
to the hosting Lisp.  I do like your strategy, so I think I'll attempt
a variation of it.

Thanks for the advice.  

-- 
"We know for certain only when we know little.  With knowlege, doubt
increases." - Goethe