[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Format strings are wrong
On Dec 27, 2003, at 11:37 PM, Alex Shinn wrote:
The SRFI process isn't just about porting, but also standardizing
libraries. One of the primary motivations of SRFI-1 was that all
Schemes provided varying levels of add-on list utilities with
incompatible API's.
It's also about getting together and producing something _better_ than
what J.
Random Schemer or Common Lisper thought up a while ago. Remember
FLUID-LET? --
there was once a SRFI about it, but it was ditched because there were
several
arguments about why it was a bad idea, and better ideas. (Actually, I
disagree
with the design of SRFI 39 (parameters), too, which could be considered
to be
one of those better ideas, but that's a different story and not really
relevant
here.) Likewise, if there are good arguments against format strings,
and there
are good alternative ideas, why should you let yourself be held down by
the old
and historically motivated idea? I don't claim that LAMBDA: The
Ultimate
Formatter is necessarily the best idea -- just as I don't think SRFI 39
is the
best idea in the related set of ideas regarding fluid variables --, but
I think
it's a better idea nevertheless.
In this case almost all Schemes at least support
SRFI-28, and many support some or all of the full Common-Lisp format,
so it is definitely something people find useful and should be
standardized. Many people feel SRFI-48 doesn't go far enough, so
there will likely be at least one more format SRFI. Perhaps that will
explicitly make the individual formatting procedures accessible as
they are in Dirk Lutzebaeck's implementation, which would give you the
option of using both styles.
Of course, and FLUID-LET is easily implemented and available in lots of
places.
But that does not make it a _good_ thing. Likewise, formatting strings
are
easily implemented and available in lots of places; indeed, there are
portable
implementations for full Common Lisp FORMAT. But CL FORMAT isn't
necessarily
a good thing.
Possibly something along the lines of simply adopting the notion that:
(str-fmt ....) [or (string ...) be extended] to accept mixed string,
symbol,
character and numerical arguments, and produces a string resulting
from the
concatenation of its argument's string equivalences
Probably best not to overload string, especially in light of recent
discussions as to just what a character is. Also, the procedure
should definitely output to a port, not a string. But even using a
port this becomes inefficient if you inline write's:
(define (write-to-string x) (with-output-to-string (cut write x)))
(fmt #t "list: " (write-to-string ls) #\newline)
and likewise for performing different number->string conversions in
the middle of a format.
Formatter procedures can work any way you like; all you need is to pass
some
different WRITE-CHAR procedure, which allows for even more
expressiveness. For
instance, you could use SHIFT & RESET to generate a stream from
FORMATTER:
(reset
(format FORMATTER
(lambda (char) (shift k (stream-cons char (k)))))
stream-null)
(Assuming the continuations that SHIFT lets you nab are n-ary. It may
also be
better to use BSHIFT & BRESET, but that's diverging from this SRFI.)
This doesn't force you to cons up intermediate strings or even
intermediate
output ports with a specialized character writer. (One might debate
about the
efficiency of SHIFT & RESET, which I don't want to right now, as it's
utterly
beside the point.)
You really have to break it apart as:
(display "list: ")
(write ls)
(newline)
which when compared with
(format #t "list: ~S\n" ls)
looks clumsy and verbose, especially when you have many of these
throughout a program almost tripling the number of lines, and
especially when you have to wrap them in begins. Format is very
concise
If we cared that much about conciseness, we'd all be using Perl or GOO.
But we
don't care _that_ much.
and not only lets you re-use the format string in parts of
your program, it lets you easily change it at runtime
The same can be said about formatter procedures.
(even in an
eval-less Scheme) and lets you load it from things like config files.
But what you're really doing there is just creating a very limited
language for
formatting; it's equivalent to having a very limited EVAL. Why not use
EVAL?
You could even write an incredibly simple EVAL that supports only
LAMBDA,
function application, and the built-in formatters.
Consider the log files of a server that need to produce customizable
output, like this from my httpd.conf:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\"
\"%{User-Agent}i\" %T %v" full
Or consider the Emacs mode-line-format string which lets you customize
(again at runtime) what appears in your mode-line. Of course, Emacs
allows nested lists as the format, which may be a consideration for
future format SRFIs rather than consider introducing artificial
nesting with ~<...~> etc.
Formatter procedures can easily be arbitrarily nested however you like.
By keeping the logic in one place format is also much better suited to
things like i18n, and can provide more efficient control over
columnating and padding.
And you don't need to remember obscure formatting directive syntax with
obscure
single-character main names and strange syntax to go around it (SRFI
29's ~@*,
anyone?). Column & padding control are especially easy when you've got
full
Scheme at your fingertips with which to write formatting routines. It
would be
trivial to write a FORMAT/PADDING routine that would keep track of the
columns
that the sub-formatters write.
All around, format is more consice [sic], more efficient, and more
flexible
than the alternatives. That doesn't mean we couldn't use some more
functional interfaces, but they will likely not replace format.
Concise [sic], I concede; but do you really want _that_ much
conciseness? I'd
say no, or, as I said, we'd all be using Perl or GOO. (OK, that's a
bit of an
overstatement, but you get the idea.)
--
Alex