<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><span dir="ltr"><<a href="mailto:pjb@informatimago.com" target="_blank">pjb@informatimago.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
Erik Pearson <<a href="mailto:erik@adaptations.com">erik@adaptations.com</a>> writes:<br>
<br>
> Never one to not jump to my own defense or fail to flog a dead<br>
> equine, and almost always finding that this is a mistake, I<br>
> nevertheless venture one last flail. When researching the etymology<br>
> of the construction "if peek-char is not supplied or nil", I find in<br>
> the MacLisp manual the precedent for this "variable arity<br>
> polymorphism based on type" in the definition of "read":<br>
><br>
> (READ [file] [eofval])<br>
><br>
> If only one argument is given, it may be either stream or eofval. If<br>
> it is a file, an sfa, or NIL, it will be assumed to be stream;<br>
> otherwise, it will be taken as eofval.<br>
><br>
> If file is not supplied or is NIL, the default input stream as<br>
> controlled by the variables ^Q, INFILE, and TYI will be used.<br>
><br>
> Interesting, perhaps, to an infinitiesimal sliver of humanity.<br>
<br>
<br>
However, this kind of function signature cannot be specified with CL<br>
lambda lists, that is, the parsing of the arguments for those functions<br>
cannot be performed by cl:defun.<br>
<br>
You would have to write something like:<br>
<br>
<br>
    (defun maclisp:read (&rest arguments)<br>
      (let ((file   (maclisp::internal-select-stream maclisp:^q maclisp:infile maclisp:tyi))<br>
            (eofvar nil))<br>
       (case (length arguments)<br>
        ((0))<br>
        ((1)  (typecase (first arguments)<br>
                 (stream (setf file   (first arguments)))<br>
                 (t      (setf eofval (first arguments)))))<br>
        ((2)  (setf file   (first arguments))<br>
              (setf eofval (second arguments)))<br>
        (otherwise (error "Too many arguments")))<br>
      (maclisp::internal-read file eofvar)))<br>
<br>
<br>
Now, one could write an extended defun macro that would take a lambda<br>
list specification that would include the types and default values of<br>
the arguments and expand to something similar to the above.  For<br>
example:<br>
<br>
    (extended:defun maclisp:read (&typed-optional<br>
                                  (file   stream (maclisp::internal-select-stream<br>
                                                  maclisp:^q maclisp:infile<br>
                                                  maclisp:tyi))<br>
                                  (eofvar boolean nil))<br>
      (maclisp::internal-read file eofvar))<br>
<br>
<br>
This wouldn't be too difficult.  However, it would have dire<br>
consequences to the Common Lisp programming style, because it would<br>
require much more control on the type of the arguments of functions,<br>
ie. on the type of all the values in the program.<br>
<br>
<br>
For example, if you write:<br>
<br>
    (cl:defun example:read (&optional<br>
                              (file (maclisp::internal-select-stream<br>
                                     maclisp:^q maclisp:infile<br>
                                     maclisp:tyi))<br>
                              (eofvar nil))<br>
      (example::internal-read file eofvar))<br>
<br>
you can pass to example:read any value, even not a strem, to the file<br>
parameter.  There's nothing in example:read that really requires file to<br>
be a stream, since file is only used as a parameter to<br>
example::internal-read, the typing of file is defered to this function.<br>
In effect, what we have here is a generic function (in the general<br>
sense, not that of CL generic-function), not a function restricted to<br>
stream types.  In practice in CL, it's used to let one pass T or NIL, or<br>
a string with a fill-pointer,   instead of a stream to such functions as<br>
FORMAT, with a meaning assigned to those values.<br>
<br>
    (let ((out (make-array 100 :adjustable t :fill-pointer 0<br>
                               :element-type 'character)))<br>
      (format out "Hello ~A" 'world)<br>
      out)<br>
    --> "Hello world"<br>
<br>
This wouldn't be possible anymore if FORMAT was defined with<br>
&typed-optional arguments.<br>
<br>
In the worst case, they would define a FORMAT-DESTINATION type to wrap<br>
the various cases:<br>
<br>
    (format (make-instance 'format-specification :string-target out)<br>
            "Hello ~A" 'world)<br>
<br>
    (format (make-instance 'format-specification :stream *query-io*)<br>
            "Hello ~A" 'world)<br>
<br>
    (format (make-instance 'format-specification :string-result t)<br>
            "Hello ~A" 'world)<br>
<br>
    (format (make-instance 'format-specification :symbolic-stream t)<br>
            "Hello ~A" 'world)<br>
<br>
just to be able to write also:<br>
<br>
    (format "Hello ~A" 'world)<br>
<br>
!<br>
<br>
Go down this slope, and you end up with Java!<br>
<span class="HOEnZb"><font color="#888888"><br></font></span></blockquote><div><br></div><div>Heaven forbid!</div><div><br></div><div>Very interesting, thanks for the exposition, it has gone into the ol' hopper.</div>
<div><br></div><div>I was thinking more from the function-specification point of view. That is, if a function specification text makes a promise about things like domain and range, interpretation of arguments, effect on the internal logic from different argument types or values, side effects, exceptions and so forth, as a user one must rely on that alone. However, one would hope that such promises are practical, that the logic is clearly stated, unambiguous, and easy to follow, all possible use cases exhausted, and so forth. I understand that sometimes CL library function arguments are defined as the value acceptable to some other function, but I still expect the library function to do the "right thing" for the programmer.</div>
<div><br></div><div>In any case, as you point out CL gives us tools to implement something like this, i.e. &optional, typecase, and argument-supplied-p. They allow us to create functions which  express polymorphism that is not explicitly provided by defun. Would you agree? (I'm no expert on argument mechanics.)</div>
<div><br></div><div>Something like peek-char is a little extreme in my experience, in terms of expressing the its ancestry. For example, my surmise is that earlier Lisps like Maclisp, without keyword arguments, were forced to use fixed order optional arguments, employing default values and filler nil values. In the case of peek-char, I'm sure that most usage is (peek-char nil stream nil), so today that construction looks a little silly. A quick scan of the CLHS shows that by far most usage of the phrase "is not supplied or nil" refers to keyword arguments not optional ones.</div>
<div><br></div><div>Regarding your example, format. I do buy your point, it introduces a mess, and am not really rooting for multi-artiy functions as we have discussed (i.e. skipping predecessor arguments.) </div><div><br>
</div><div>Erik.<br></div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="HOEnZb"><font color="#888888">
<br>
--<br>
__Pascal Bourguignon__<br>
<a href="http://www.informatimago.com/" target="_blank">http://www.informatimago.com/</a><br>
"Le mercure monte ?  C'est le moment d'acheter !"<br>
<br>
_______________________________________________<br>
Openmcl-devel mailing list<br>
<a href="mailto:Openmcl-devel@clozure.com">Openmcl-devel@clozure.com</a><br>
<a href="http://clozure.com/mailman/listinfo/openmcl-devel" target="_blank">http://clozure.com/mailman/listinfo/openmcl-devel</a><br>
</font></span></blockquote></div><br><br clear="all"><div><br></div>-- <br>Erik Pearson<br>Adaptations<br>;; web form and function 
</div></div>