5
votes

I'm fairly new to Lisp, and I've run into a printing issue. I have one function which does printing to the standard output (among other things). I want to then run this function through another function where it still runs the same but instead nothing gets printed to the standard output.

Here is a simple example of what I mean. I have the following two functions described:

(defun does-printing()
  (print "This goes to standard output."))

(defun run-other-function (function)
  (funcall function)
  (values))

Here's a dribble of what happens when I run this,

;; Dribble of #<IO TERMINAL-STREAM> started on 2014-10-05 21:49:49.
#<OUTPUT BUFFERED FILE-STREAM CHARACTER #P"example.out">
[7]> (run-other-function #'does-printing)

"This goes to standard output." 

[8]> (dribble)
;; Dribble of #<IO TERMINAL-STREAM> finished on 2014-10-05 21:50:09.

Note that the printing function still prints to the standard output. It'd like to be able to suppress this printing somehow when running does-printing through run-other-function. I have tried many different variations of phrasing of my problem when searching for solutions, but nothing is getting at what I would like to do.

2
How about just adding an optional argument "print-output" to does-printing?Paul Richter
I do like this solution for some situations, but it won't work for my needs. I've been writing a basic unit test structure for myseltfjust to get some practice with the language, so I can't worry about putting optional arguments for every potential print.Nyles

2 Answers

11
votes

The simplest solution is to create an empty broadcast stream.

(with-open-stream (*standard-output* (make-broadcast-stream))
  (call-some-function-1)
  ...
  (call-some-function-n))

If a broadcast stream has no component stream all output will be discarded. Above binds the *standard-output* to such a stream. This does not cons up any data and it is portable.

3
votes

You can just redirect your standard-output to some place. For example into /dev/null if you have one in your operating system. It looks like very idiomatic UNIX-way output suppressing.

Note, that you shouldn't set it to NIL, because print will signal type error in this case.

(defun does-printing()
  (print "This goes to standard output."))

(defun run-other-function (function)
  (with-open-file (*standard-output*
                    "/dev/null"
                    :direction :output
                    :if-exists :supersede)
    (funcall function)
    (values)))

CL-USER> (run-other-function #'does-printing)
; No value

Other option (and it may be better) is to use with-output-to-string, so you can capture this output value or just ignore it. Is think it's better, because why to do IO if we don't need it, and also it must work on any OS.

(defun run-other-function (function)
  (with-output-to-string (*standard-output*
                           (make-array '(0)
                                       :element-type 'base-char
                                       :fill-pointer 0 :adjustable t))
    (funcall function)
    (values)))

If you doing it a lot, you can wrap it into macro or even function, to use in place of funcall.

(defun does-printing()
  (print "This goes to standard output.")
  "My result")

(defun funcall-with-no-output (fn)
  (with-output-to-string (*standard-output*
                            (make-array '(0)
                                        :element-type 'base-char
                                        :fill-pointer 0 :adjustable t))
                          (funcall fn)))

CL-USER> (funcall-with-no-output #'does-printing) 
"My result"

But i think macro will be more general and idiomatic for this case (may be I'm wrong).

(defmacro with-suppressed-output (&body body)
  `(with-output-to-string (*standard-output*
                            (make-array '(0)
                                        :element-type 'base-char
                                        :fill-pointer 0 :adjustable t))
   ,@body))

So you can call many forms in with-suppressed-output.