design patterns - lisp: dynamic scope vs explicit parameter passing -


i see 2 different patterns "output" functions in (common) lisp:

(defun implicit ()   (format t "life? don't talk me life!"))  (defun explicit (stream)   (format stream "this end in tears."))  (defun test-im-vs-ex-plicit ()   (values    (with-output-to-string (stream)      (let ((*standard-output* stream))        (implicit)))    (with-output-to-string (stream)      (explicit stream)))) 

is using dynamic scope in implicit considered bad practice or accepted use of dynamic scoping? note i'm assuming e.g. dsl build complex output, html, svg, latex or whatever , not expected different apart producing printed representation.

are there - apart style - important differences, e.g. respect performance, concurrency, or whatever?

actually can bind *standard-output* directly:

(defun test-im-vs-ex-plicit ()   (values    (with-output-to-string (*standard-output*)   ; here      (implicit))    (with-output-to-string (stream)      (explicit stream)))) 

there no real simple answer. advice:

use stream variables, makes debugging easier. appear on argument lists , easier spot in backtrace. otherwise need see in backtrace there dynamic rebinding of stream variable.

a) nothing pass?

(defun print-me (&optional (stream *standard-output*))   ...) 

b) 1 or more fixed arg:

(defun print-me-and-you (me &optional (stream *standard-output*))   ...) 

c) 1 or more fixed args , multiple optional args:

(defun print-me (me                  &key                  (style  *standard-style*)                  (font   *standard-font*)                  (stream *standard-output*))   ...) 

note this:

now assume (implicit) has error , break loop, debugging repl. what's value of standard-output in break loop?

cl-user 4 > (defun test ()               (flet ((implicit ()                        (write-line "foo")                        (cerror "go on" "just break")                        (write-line "bar")))                 (with-output-to-string (stream)                   (let ((*standard-output* stream))                     (implicit))))) test  cl-user 5 > (compile 'test) test nil nil  cl-user 6 > (test)  error: break   1 (continue) go on   2 (abort) return level 0.   3 return top loop level 0.  type :b backtrace or :c <option number> proceed. type :bug-form "<subject>" bug report template or :? other options.  cl-user 7 : 1 > *standard-output* #<system::string-output-stream 40e06ad80b>  cl-user 8 : 1 > (write-line "baz") "baz"  cl-user 9 : 1 > :c 1 "foo baz bar " 

above see in lispworks or sbcl. here have access real program's binding, using output functions during debugging have effects on stream.

in other implementations *standard-output* rebound actual terminal io - example in clozure cl , clisp.

if program not rebind *standard-output* there less confusion in cases. if write code, think more useful in repl environment - different languages, there less interactive debugging on repls , break loops...


Comments

Popular posts from this blog

angularjs - ADAL JS Angular- WebAPI add a new role claim to the token -

php - CakePHP HttpSockets send array of paramms -

node.js - Using Node without global install -