common-lisp

Access constant defined in a file loaded with load


I have this lisp file:

(load "constants.lisp")

(defpackage :my-package
  (:use :cl))

(in-package :my-package)

(format t "Constant value: ~A~%" my-constants:+my-constant+)

And this is constants.lisp (same directory):

(defpackage :my-constants
  (:use :cl)
  (:export :+MY-CONSTANT+))

(in-package :my-constants)

(defconstant +MY-CONSTANT+ "some value")

I get this error when I do C-c C-k on Sly:

There is no package named "MY-CONSTANTS" .
[Condition of type CCL::NO-SUCH-PACKAGE]

How can I load some constant defined in other file? Something like import in other languages. Thanks!!


Solution

  • C-c C-k Is A Compiling Command

    The problem is that C-c C-k does not run the file, but compiles it.

    So to ensure the loading is executed at compile level, you have to load by:

    (eval-when (:compile-toplevel :load-toplevel :execute)
      (load "constants.lisp"))
    

    C-c C-l re-loads the file

    But I think what you actually wanted to do is sly-load-file just loading the current file.

    This you can do by: C-c C-l and press RETURN when it asks for whether to load this current file's path.

    And then, it should work with the simple (load "constants.lisp") without the eval-when expression around it!

    The only annoying thing will be that you are loading a constant - and therefore it will first activate the handler system (like and error) and ask you what to do, since you are re-loading a constant (constants are not thought to be changed during a session thus also not re-defined).

    Perhaps Don't Use A Constant?

    If it is not absolutely necessary to declare it as a constant, I would just use (defparameter *my-constant* "my value") and treat it as a special variable which you - by your own convention - doesn't change any more for the rest of the session.

    Or, you define (defvar *my-constant* "my value") and the next time defvar on the same variable is run - it will be ignored - so it is some kind of a constant - no reloading of the variable.

    Use ASDF To Control Loading Fully

    When you are trying to write a package which loads the my-constants package, then you should use asdf.

    With a project structure like

    my-project/
    │── my-project.asd       # ASDF system definition
    │── my-constants.lisp    # Constants package
    │── my-package.lisp      # Main package
    └── main.lisp            # Entry point (optional)
    

    and my-project.asd:

    (asdf:defsystem "my-project"
      :description "An example ASDF system with multiple files"
      :version "0.1"
      :author "Your Name"
      :license "MIT"
      :depends-on ()
      :serial t      ;; loading of files in the given order:
      :components ((:file "my-constants")
                   (:file "my-package")
                   (:file "main")))
    

    my-constants.lisp:

    (defpackage :my-constants
      (:use :cl)
      (:export +MY-CONSTANT+))
    
    (in-package :my-constants)
    
    (defconstant +MY-CONSTANT+ 42)
    

    my-package.lisp:

    (defpackage :my-package
      (:use :cl :my-constants))
    
    (in-package :my-package)
    
    (defun print-constant ()
      (format t "Constant value: ~A~%" +MY-CONSTANT+))
    

    And finally main.lisp with the final package:

    (defpackage :my-project
      (:use :cl :my-package))
    
    (in-package :my-project)
    
    (defun main ()
      (format t "Running my-project...~%")
      (print-constant))
    
    ;;; Call main automatically when loading:
    (main)
    

    Or use if you want to use from main.lisp also the constants:

    (defpackage :my-project
      (:use :cl :my-package :my-constants))
    
    (in-package :my-project)
    
    (defun main ()
      (format t "Running my-project...~%")
      (print-constant)
      (format t "Constant: ~A%~%" my-constants:+MY-CONSTANT+))
    
    ;;; Call main automatically when loading:
    (main)
    

    Manually enforce loading of the file

    The most primitive solution and simplest solution would be to manually load both files.

    my-loader.lisp:

    (load "my-constants.lisp")
    (load "my-package.lisp")
    

    my-constants.lisp:

    (defpackage :my-constants
      (:use :cl)
      (:export +MY-CONSTANT+))
    
    (in-package :my-constants)
    
    (defconstant +MY-CONSTANT+ 42)
    

    my-package.lisp:

    (defpackage :my-package
      (:use :cl :my-constants))
    
    (in-package :my-package)
    
    (defun print-constant ()
      (format t "Constant value: ~A~%" +MY-CONSTANT+))
    

    And then finally call in the REPL:

    (load "my-loader.lisp")
    ;; and then you can call
    (my-package::print-constant)  ;; since not externalized, we use `::`