common-lispconventionsasdf

ASDF:REQUIRE-SYSTEM -> Deprecated and to avoid und all circumstances, or not?


I'm starting to delve into Common Lisp's ASD Facility and am wondering, whether asdf:require-system is still useful in the described way.

That is: With this function it is "appropriate to load code that is not being modified during the current programming session."

Say, I would like to use cl-str in my project. I won't modify that system, so it appears natural to me to load it with (asdf:require-system :str).

This also could highlight at a glance: Those systems I load with asdf:load system are the ones I am developing here. The ones I only require for development are these.

But using it results in:

WARNING:
 DEPRECATED-FUNCTION-STYLE-WARNING: Using deprecated function 
 ASDF/OPERATE:REQUIRE-SYSTEM -- please update your code to use 
 a newer API.
The docstring for this function says:
Ensure the specified SYSTEM is loaded, passing the KEYS to OPERATE, 
but do not update the system or its dependencies if it has already 
been loaded.

So, are there more then cosmetic drawbacks (warnings which can be ignored) - maybe because cl:require is deprecated? But, cl:*modules* and its facility is also deprecated according to the standard. But my cl:*modules* in SBCL with Slime contains at this moment:

("SWANK-ARGLISTS" "SWANK-FANCY-INSPECTOR" "SWANK-FUZZY" "SWANK-C-P-C"
 "SWANK-UTIL" "SWANK-MACROSTEP" "SWANK-PRESENTATIONS" "SWANK-REPL"
 "SWANK-PACKAGE-FU" "SWANK-TRACE-DIALOG" "SWANK-ASDF" "SWANK-SPROF" "SB-SPROF"
 "SWANK-HYPERDOC" "SWANK-INDENTATION" "SB-CLTL2" "SB-INTROSPECT"
 "SB-BSD-SOCKETS" "ASDF" "asdf" "UIOP" "uiop" "SB-POSIX")

Most important to me: Is there a disadvantageous behaviour of asdf:require-system? Second important: Is it so unconventional to use it that other developers would turn up their noses at this?

EDIT: Actually, I do not see, how to comment the good answer of Gwang-Jin Kim below effortlessly. So, I just put it here as an amendment.

This was very helpful, thank you so much.

First of all because of your further hints. And second of all: My answer to your question "why not reloading the system" finally helped me to do the right search again. And I found this:

https://github.com/fare/asdf/blob/master/doc/best_practices.md#require

And now, I have both: An answer and more material to learn. :-)


Solution

  • Why not reloading the system with:

    (asdf:load-system :str :force t)
    

    ?

    A cosmetic problem might be the many compiler messages - you can suppress them with this context macro:

    (defmacro with-silenced-compilation (&body body)
      "Macro to allow certain settings -
       - silenced *compile-verbose*
       - silenced *compile-print*
       - and certain *debug-print-variable-alist* settings"
      `(let ((*compile-verbose* nil)
             (*compile-print* nil))
         ,@body))
    

    Then, loading is better - more silent:

    (with-silenced-compilation
      (asdf:load-system :str :force t))
    

    I don't know asdf:require-system that well that I can answer all your questions. But when I had to reload a package during runtime - after changing some global variables (in that package, compilation was necessary - and therefore we had to reload it and recompile it every time something was set to a different value) - I used asdf:load-system and it perfectly worked fine - that's why I don't see the necessity to use a deprecated or soon-to-be-deprecated function.

    If you need by the way some permanent variables - which should stay after reloading, you can dump all persistent variables into a file and reload them after the reloading. By this, it would be as if your system is just running smoothly further (at least if these persistent information is not so much).

    I used such functions:

    ;; --------------------- file handling ------------------------------- ;;
    
    (defun copy-file-content (source-file target-file)
      "Replace the content of target-file by the content of source-file."
      (with-open-file (in source-file :direction :input)
        (with-open-file (out target-file :direction :output :if-exists :supersede)
          (loop for line = (read-line in nil nil)
            while line
            do (write-line line out)))))
    
    
    (defun save-to-file (list filename)
      (with-open-file (out filename :direction :output :if-exists :supersede :if-does-not-exist :create)
        (format out "~S" list)))
    
    
    (defun read-from-file (filename &optional (default '()))
      (if (probe-file filename)  ; Check if the file exists
          (with-open-file (stream filename :direction :input)
            (read stream))
          ;; If file doesn't exist, create it with the default values
          (progn
            (save-to-file default filename)
            default)))
    
    (defparameter *globals-file* 
      (merge-pathnames "vals.lisp" (get-package-root :wouldwork))
      "In the vals.lisp file of this package the values of parameters
         are stored as a list.
       This should preserve when reloading the package for problems
       the values of these global variables. The user should not
       have to worry about the changes of these values after reloading.")
    
    
    (defun display-globals ()
      (format t "~&*problem-name* ~A~% 
                   *depth-cutoff* ~A~%*tree-or-graph* ~A~%*solution-type* ~A~%
                   *progress-reporting-interval* ~A~%*randomize-search* ~A~%*branch* ~A~%*probe* ~A~%                                    *debug* ~A~2%"
               ;*keep-globals-p*
                *problem-name* *depth-cutoff* *tree-or-graph* *solution-type*
                *progress-reporting-interval* *randomize-search* *branch* *probe*
                *debug*)) ;*threads*
                ;*features*))
    
    
    (defun reset-parameters ()
       "Resets global parameters to defaults"
      (setf *problem-name* 'unspecified *depth-cutoff* 0 *tree-or-graph* 'graph
            *solution-type* 'planning *progress-reporting-interval* 100000
            *randomize-search* nil *branch* -1 *probe* nil *debug* 0)
      (setf *features* (remove :ww-debug *features*))
      (display-current-parameters))
    
    
    (defun save-globals ()
      "Save the values of the globals in the vals.lisp file."
      (save-to-file (list ;*keep-globals-p*
                          *problem-name* *depth-cutoff* *tree-or-graph* *solution-type*
                          *progress-reporting-interval* *randomize-search* *branch* *probe* *debug*
                          #|*features* *threads*|#)
                    *globals-file*)) ;; this stores global var values
    
    (defun set-globals (&key ;(keep-globals-p *keep-globals-p*)
                             (problem-name *problem-name*)
                             (depth-cutoff *depth-cutoff*)
                             (tree-or-graph *tree-or-graph*)
                             (solution-type *solution-type*)
                             (progress-reporting-interval *progress-reporting-interval*)
                             (randomize-search *randomize-search*)
                             (branch *branch*)
                             (probe *probe*)
                             (debug *debug*))
                             ;(features *features*))
                             ;(threads *threads*))
      "Set multiple globals at once in keywords argument format."
      (setf ;*keep-globals-p* keep-globals-p
            *problem-name* problem-name
            *depth-cutoff* depth-cutoff
            *tree-or-graph* tree-or-graph
            *solution-type* solution-type
            *progress-reporting-interval* progress-reporting-interval
            *randomize-search* randomize-search
            *branch* branch
            *probe* probe
            *debug* debug)
            ;*features* features)
            ;*threads* threads)
      (save-globals))
    
    
    ;; the `keep-globals-p` variable decides over whether the values of `vals.lisp`
    ;; get transferred to the current session.
    
    (defun read-globals ()
      "Read and setf values for global variables from vals.lisp file."
      (let ((default-values (list nil 0 'tree 'first 100000 nil -1 nil 0)))  ; *features*)))
        (destructuring-bind 
            (;keep-globals-p
             tmp-problem-name tmp-depth-cutoff tmp-tree-or-graph tmp-solution-type
             tmp-progress-reporting-interval tmp-randomize-search tmp-branch tmp-probe tmp-debug)  ; tmp-features)
            (let ((vals (or (ignore-errors (read-from-file *globals-file*))
                            default-values)))
              vals)
              ;(if (= (length vals) (length default-values)) ;; because we change globals often number of values in vals.lisp can differ
              ;    vals
              ;    (progn
              ;      (format t "Using `default-values` (length ~A) because length of vals.lisp differs (~A).~%"
              ;              (length default-values) (length vals))
              ;      default-values)))
          ;(when keep-globals-p
            (setf ;*keep-globals-p* keep-globals-p
                  *problem-name* tmp-problem-name
                  *depth-cutoff* tmp-depth-cutoff
                  *tree-or-graph* tmp-tree-or-graph
                  *solution-type* tmp-solution-type
                  *progress-reporting-interval* tmp-progress-reporting-interval
                  *randomize-search* tmp-randomize-search
                  *branch* tmp-branch
                  *probe* tmp-probe
                  *debug* tmp-debug))))
                  ;*features* tmp-features))))
    

    You can see their usage here: https://github.com/davypough/wouldwork/blob/main/src/ww-interface.lisp

    (which is the repo from a friend - I helped back then to set this up).