Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use package shadowing symbols

For instance I have this package definition which shadows COMMON-LISP:LISTEN :

(defpackage :shadows
  (:use :common-lisp)
  (:shadow #:listen)
  (:export #:listen))

And then I want to use this package from another package, say

(defpackage :light
  (:use :common-lisp
        :shadows))

What is the purpose of shadow if I cannot actually override Common Lisp symbols when using the package ?

like image 884
thodg Avatar asked Feb 04 '23 06:02

thodg


2 Answers

Simple

The :shadow argument to defpackage affects the definition of the package shadows, not the later use of shadows in light.

You need to use shadowing-import:

(defpackage #:shadows
  (:use #:common-lisp)
  (:shadow #:listen)
  (:export #:listen))
(defpackage #:light
  (:shadowing-import-from #:shadows #:listen)
  (:use #:common-lisp #:shadows))
(eq 'light::listen 'shadows:listen)
==> T
(describe 'light::listen)
==>
SHADOWS:LISTEN is the symbol SHADOWS:LISTEN, lies in #<PACKAGE SHADOWS>,
is accessible in 2 packages LIGHT, SHADOWS.

 #<PACKAGE SHADOWS> is the package named SHADOWS.
 It imports the external symbols of 1 package COMMON-LISP and
 exports 1 symbol to 1 package LIGHT.

Bulk

If you need to do bulk shadowing, you would need to use individual functions (make-package, import, export, shadow, use-package):

(defparameter *my-shadow-list* '(#:car #:cdr))
(make-package '#:my-package :use nil)
(import *my-shadow-list* '#:my-package)
(export *my-shadow-list* '#:my-package)
(shadow *my-shadow-list* '#:my-package)
(use-package '#:cl '#:my-package)
(make-package '#:my-user)
(shadowing-import *my-shadow-list* '#:my-user)
(use-package '(#:cl #:my-package) '#:my-user)
(describe 'my-user::car)
==>
MY-PACKAGE:CAR is the symbol MY-PACKAGE:CAR, lies in #<PACKAGE MY-PACKAGE>,
is accessible in 2 packages MY-PACKAGE, MY-USER.

 #<PACKAGE MY-PACKAGE> is the package named MY-PACKAGE.
 It imports the external symbols of 1 package COMMON-LISP and
 exports 2 symbols to 1 package MY-USER.

You might find macroexpand-1 useful in decising how get where you need to go:

(macroexpand-1 '(defpackage #:light
                 (:shadowing-import-from #:shadows #:listen)
                 (:use #:common-lisp #:shadows)))
==>
(EVAL-WHEN (:COMPILE-TOPLEVEL :LOAD-TOPLEVEL :EXECUTE)
 (SYSTEM::%IN-PACKAGE "LIGHT" :NICKNAMES 'NIL :USE 'NIL :CASE-SENSITIVE NIL
  :CASE-INVERTED NIL)
 (SYSTEM::SHADOWING-IMPORT-CERROR "LISTEN" "SHADOWS" NIL "LIGHT")
 (USE-PACKAGE '("COMMON-LISP" "SHADOWS") "LIGHT") (FIND-PACKAGE "LIGHT")) ;
T

PS. Shadowing ANSI CL standard names is not a very good idea for your code readability.

like image 128
sds Avatar answered Feb 23 '23 10:02

sds


OK so there actually is a handy way to do this.

In my many-shadows package I export a shadowing-import-from function. It looks like this :

(defun shadowing-import-from ()
  `(:shadowing-import-from :many-shadows
     ,@(package-shadowing-symbols :many-shadows)))

And then, in the light defpackage I just READ-EVAL it :

(defpackage :light
  (:use :common-lisp
        :many-shadows)
  #.(many-shadows:shadowing-import-from))

So it is explicit that something was shadowed and that I want to use the symbols from many-shadows in priority. And it is short enough to be documented and used pragmatically.

Thank you @sds for pointing me to :shadowing-import-from.

like image 44
thodg Avatar answered Feb 23 '23 10:02

thodg