[gambit-list] Thinking of changing pp

Christian Jaeger christian at pflanze.mine.nu
Sun Jun 1 19:24:00 EDT 2008

Hello Marc & co,

My reply is a bit long, but has two parts, a first about the |&|
symbol, and a second about my ways to do "printf debugging". I care
about the former part more, so if you skip parts of this message,
please do so with the second.

Marc Feeley wrote:
> Perhaps I shouldn't change pp and instead use a different short name, 
> but which one?  It could be a special symbol, for example:
> (define (square x)
>    (& x result: (* x x)))

I'm hoping that you won't use |&| for that, since I've long been using
this symbol as a shortcut for creating a thunk.

(define-macro (& first . rest)
  `(lambda() ,first , at rest))

The & seemed like the best character to me for that purpose;

- in Perl, it's the sigil for subroutine slots in the toplevel
- in C, it's taking a pointer
- in sh/bash, you use it for suspension of jobs

Ok, those are all not really the same thing. But still related to
"suspending execution", to "taking a pointer to something" (give a
pointer to code instead of executing it and giving the result); and
Perl decided to designate the & for routines, so why shouldn't I do so
too (regardless of Perl being ugly and possibly evil).

I've already started using a & as appendix to macros whose ~sole
purpose is taking a thunk of their body; like

(define-macro (with-output-to-string& . body)
     (lambda () ;; could have used & here of course, too
       , at body)))

> But special symbols are in short supply and best left for future 
> extensions and user needs.  That's why I like pp.  It is easy to type 
> and the meaning is clear.
> What do you think?

I don't think I would ever have been bitten by pp not being a
procedure, so I won't really care, but I'm not sure of the benefit
either; |error| isn't a special form either, and there you've got some
machinery for dealing with multiple values already. If you think you
want to improve pp, you should improve error as well, or at least make
their behaviour of creating the message string match. Now I think
making error a macro would really not such a good idea, so I think the
non-dieing counterpart of |error| should not be one either.  But I've
already choosen to call that counterpart something else already:
warn. And for the purpuse of displaying a value while returning it,
I'm using a procedure I'm calling pp-through.

Here they are:

(define no-value (gensym))

(define-bothtimes (pp-through a #!optional (b no-value))
  (define port (current-output-port))
  ;; hm ugly, during load, chjmodule outputs to stdout, so this is the
  ;; relevant port to print to. using current-error-port doesn't work
  ;; as intended.
  (if (> (output-port-column port) 1)
      (newline port))
  (if (eq? b no-value)
    (pp a port)
    (display a port)
    (pp b port)

(define (warn . l) ;; made it so that it acts the same as gambit's error
  (if (pair? l)
      (let ((p (current-error-port)))
    (display (car l) p)
    (let loop ((l (cdr l)))
      (if (pair? l)
        (write-char #\space p)
        (write (car l) p)
        (loop (cdr l)))
          (newline p))))
      (error "cj-env#warn called without arguments")))

(I must admit that I don't know anymore why I did put that "hm ugly"
comment above.)

Here they are in action:

 > (list result: (warn "Hello" "World"))
Hello "World"
(result: #!void)
 > (list result: (let ((i 1)(res '(4 3 2))) (warn "hey my values are:" i: i
res: res)))
hey my values are: i: 1 res: (4 3 2)
(result: #!void)


 > (list result: (pp-through '(hey "ya")))
(hey "ya")
(result: (hey "ya"))
 > (list result: (pp-through foo: '(hey "ya")))
foo:(hey "ya")
(result: (hey "ya"))
 > (list result: (pp-through "foo:" '(hey "ya")))
foo:(hey "ya")
(result: (hey "ya"))

(I'm not sure why I don't emit a space after the first argument in
pp-through, it would be consistent with |warn| to do so; but I'm using
the two-argument form rarely anyway.)

I'm using |pp-through| for those cases where I want to show a value
being returned (optionally with a description before it); that's
especially the case in macros to show the code being generated.

I'm using |warn| for those "printf debugging" cases where I want to
show some statement or one or more values as a side effect -- if this
happens to be in a non-sequence position, this would need me to wrap
it with a begin, like:

(let lp ((i 10) (res '()))
  (if (negative? i)
	(warn "in lp" i: i res: res)
	(lp (- i 1) (cons i res)))))

BTW note that in a case like this the warn couldn't be in return
position anyway, so a "wrap-a-function-around" approach like
pp-through wouldn't work.  (Could you do it with a macro/special form? 
Not sensibly I think.)

For me that distinction between multi-value-print-capability-but-
no-return-value and only-one-value-but-return-it works well
enough. (The first case returning #!void may actually be a hint for
cases where a warn statement is in a return position by accident.)

The only incentive to turn |warn| into a macro for me had been to
automatically also show the source code position of the warn
statement, like perl's warn does (unless perl's warn is given a
message which ends in a newline, which suppresses the line number /
file display), but I've not (yet) bothered enough to do that. And
possibly it would be better done by inspecting the continuation of the
warn call dynamically, not by making it syntax -- as long as warn is
not in tail position (which it never should be) that would work, after
all (and Perl did choose that dynamic approach, too, although since
Perl doesn't have TCO, there's never a case where finding the location
would fail).

What I can't do cleanly with |warn| (but neither can I do it with
|error|) is putting some text later in the message, like:

 (warn "Foo is" foo "while bar is actually" bar)

will output the third argument as quoted string, of course. So maybe
taking this further, inevitably by making it a special form/macro,
would be worthwhile. Maybe call it |warn&| then, and also offer an
analogous |error&| form? I don't see that problem in the
return-the-value case since there you usually don't have the need to
put anything *after* the value. The only thing a macro would bring you
in that latter case is to show the code which generated the value
automatically, but if that code is a big form (as opposed to just a
variable reference), it would clutter up the output.


More information about the Gambit-list mailing list