From: Matthew Danish
Subject: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <87y8dun8ua.fsf@mapcar.org>
I was playing around with a macro similar to check-type when it
occurred to me that I was not sure how to properly implement it safely
with regard to side-effects.  I macroexpanded check-type to see how it
worked, but found it to be possibly evaluating the place multiple
times (on Allegro 7.0).  The definition of check-type says that the
place will be evaluated once under normal rules; and possibly again if
the store-value restart is used.  I concocted the following test case
which does not use store-value:

(defun test-check-type ()
  (let ((array (make-array 100 :initial-element nil))
        (index 0))
    (handler-case 
       (check-type (aref array (incf index)) string)
     (error () index))))

ACL 7.0, (test-check-type) => 3
SBCL 0.8.16.5, (test-check-type) => 2
CMUCL 19a, (test-check-type) => 2
CLISP 2.30, (test-check-type) => 1

I believe that 1 is the correct answer here.

-- 
;; Matthew Danish -- user: mrd domain: cmu.edu
;; OpenPGP public key: C24B6010 on keyring.debian.org

From: Steven M. Haflich
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <FEXPd.4830$ZZ.1472@newssvr23.news.prodigy.net>
Matthew Danish wrote:
> The definition of check-type says that the
> place will be evaluated once under normal rules; and possibly again if
> the store-value restart is used.

Huh?  This is _not_ what the ANS says.  It says:

  For check-type ... subforms of the place are evaluated once as in (1),
  but might be evaluated again if the type check fails ...

In your example the type check fails, and in some implementations the
subforms of place _are_ evaluated more than once, in others not.  There
is no dependency on the restart being invoked.  Your example invokes no
restartss, but certainly the type check is violated.  What's the problem,
other than your misreading of the ANS?

(defun test-check-type ()
   (let ((array (make-array 100 :initial-element nil))
         (index 0))
     (handler-case
        (check-type (aref array (incf index)) string)
      (error () index))))

The specification of check-type in the ANS is poorly crafted and admits
all sorts of unnecessary behavior and performance, but that's probably
because check-type was in practice never adopted as a reasonable and
common thing to put in production code.  No oneni X3J13 thought it
particularly important to get it right.  Sad, but I think true.
From: Matthew Danish
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <87u0ofm1lb.fsf@mapcar.org>
"Steven M. Haflich" <·················@alum.mit.edu> writes:

> Matthew Danish wrote:
> > The definition of check-type says that the
> > place will be evaluated once under normal rules; and possibly again if
> > the store-value restart is used.
> 
> Huh?  This is _not_ what the ANS says.  It says:

Sorry, it is precisely what it says.  See the definition of Macro CHECK-TYPE:

  The first time place is evaluated, it is evaluated by normal
  evaluation rules. It is later evaluated as a place if the type check
  fails and the store-value restart is used; see Section 5.1.1.1
  (Evaluation of Subforms to Places).

 
>   For check-type ... subforms of the place are evaluated once as in (1),
>   but might be evaluated again if the type check fails ...


In Section 5.1.1.1 it says

  3. For check-type, ctypecase, and ccase, subforms of the place are
     evaluated once as in (1), but might be evaluated again if the
     type check fails in the case of check-type or none of the cases
     hold in ctypecase and ccase.

I understand the meaning of "might be evaluated again" to refer to the
situation stated in the prior definition.

> The specification of check-type in the ANS is poorly crafted and admits
> all sorts of unnecessary behavior and performance, but that's probably
> because check-type was in practice never adopted as a reasonable and
> common thing to put in production code.  No oneni X3J13 thought it
> particularly important to get it right.  Sad, but I think true.

This is unfortunate.  I do use it in production code as a guard
against invalid values.  Since it signals a correctable error,
automatic handling can be put in place if necessary.  I don't
anticipate using it in such a strange fashion with side-effects, but
in the interest of correctness I bring this up.

The larger question, though, is how to write a macro like this which
only evaluates /place/ once.  In my particular case, I am writing a
macro which does coercion of values into types based on a table.  For
example, if I expect a double-float, then I will try to convert the
value into a double-float using one of a number of methods.  This is
to satisfy the demands of ORBlink which expects precisely the correct
type of value to be passed.

I can write this the simple way, where it transforms into basically:
  `(setf ,place (%convert-fn ',type ,place))
but I am interested in the correct way of doing this.

-- 
;; Matthew Danish -- user: mrd domain: cmu.edu
;; OpenPGP public key: C24B6010 on keyring.debian.org
From: Lars Brinkhoff
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <85d5v2j1lq.fsf@junk.nocrew.org>
Matthew Danish <··········@cmu.edu> writes:
> I can write this the simple way, where it transforms into basically:
>   `(setf ,place (%convert-fn ',type ,place))
> but I am interested in the correct way of doing this.

Use get-setf-expansion, possibly together with define-setf-expander.
I always forget how it works, but some experimentation will usually do
the trick.  Macroexpand-1 is your friend.
From: Kalle Olavi Niemitalo
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <878y5q5fis.fsf@Astalo.kon.iki.fi>
Lars Brinkhoff <·········@nocrew.org> writes:

> Matthew Danish <··········@cmu.edu> writes:
>> I can write this the simple way, where it transforms into basically:
>>   `(setf ,place (%convert-fn ',type ,place))
>> but I am interested in the correct way of doing this.
>
> Use get-setf-expansion, possibly together with define-setf-expander.
> I always forget how it works, but some experimentation will usually do
> the trick.  Macroexpand-1 is your friend.

With the following macros, you can write that as:

  (let ((gplace (gensym "PLACE")))
    `(symbol-macroletf ((,gplace ,place))
       (setf ,gplace (%convert-fn ',type ,gplace))))

;;; Copyright 2004 Kalle Olavi Niemitalo.
;;;
;;; Initial publication:
;;;   http://paste.lisp.org/display/2992#1
;;;
;;; Portability:
;;;   ANSI Common Lisp

(defmacro symbol-macroletf-helper (store-vars writer-form reader-form)
  (declare (ignore store-vars writer-form))
  reader-form)

(define-setf-expander symbol-macroletf-helper (store-vars writer-form
                                               reader-form)
  (let* ((new-store-vars (loop repeat (length store-vars)
                               collect (gensym)))
         (new-writer-form `(let (,@(mapcar #'list store-vars new-store-vars))
                             ,writer-form)))
    (values '() '() new-store-vars new-writer-form reader-form)))

(defmacro symbol-macroletf ((&rest bindings) &body body
                            &environment environment)
  "Like SYMBOL-MACROLET but evaluate subforms just once up front.
Also, the BODY is never considered to be at top level."
  (loop with (vars vals store-vars writer-form reader-form)
        for (symbol place) in bindings
        do (setf (values vars vals store-vars writer-form reader-form)
                 (get-setf-expansion place environment))
        nconc (mapcar #'list vars vals)
          into let*-bindings
        collect `(,symbol (symbol-macroletf-helper ,store-vars ,writer-form
                                                   ,reader-form))
          into symbol-macrolet-bindings
        finally (return `(let* (,@let*-bindings)
                           (symbol-macrolet (,@symbol-macrolet-bindings)
                             ,@body)))))
From: Kent M Pitman
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <ufyzyh7o7.fsf@nhplace.com>
Matthew Danish <··········@cmu.edu> writes:

> "Steven M. Haflich" <·················@alum.mit.edu> writes:
> 
> > The specification of check-type in the ANS is poorly crafted and admits
> > all sorts of unnecessary behavior and performance,

I don't know if I agree with smh here.

(Incidentally, I don't mean to appear defensive.  I FREELY admit that
his statement characterizes MANY of the wordings I chose elsewhere in
the ANSI CL document.  The document is uneven in quality, for many
reasons, not always the same one, but to include: time, energy,
budget, where we all were in the learning cycle, amount of available
review time, will of the committee to consider alternatives, and so
on.  But for whatever reason, it is frequently (if lamentably) true
that "poorly crafted" sums up some of the sentences I put there, and I
have to kind of adopt a really good sense of humor now that the dust
has settled when I go back and see what's been written.)

However, all in all, as I look back at the dictionary definition of 
CHECK-TYPE, I don't see that it's that bad.  I might doodle with the
presentation order slightly, but it seems to eventually get to all the
relevant parts.

I'd be curious what's actually a problem in this case, in practice.

> > but that's probably
> > because check-type was in practice never adopted as a reasonable and
> > common thing to put in production code.

I suspect this remark applies more to Franz/Allegro than the community
at large.  As far as I know, some dialects used it more than others.

Symbolics didn't tend to ever use it for numerics only because it was
in the microcode/hardware callbacks.  That is, if I remember right,
the hardware for things like addition and subtraction would
effectively either succeed or would call back to something that would
simulate having hit a software error.  But for other types, I think 
check-type was quite often used, in part because the Lisp Machine made
the correct decision to take advantage of the clear description of how the
string argument worked, and would make
 (check-type var (not constantp) "a variable")
generate nice messages like
 The value of VAR is not a variable.
rather than what LispWorks did for a long time, which would put out
something dorky like
 The value of VAR was not of type "a variable".
which didn't encourage anyone to use it.  Moreover, I think Symbolics also
had things that would allow you to make
 (check-type var (or string integer))
put out
 The variable VAR is not an integer or a string.
which again encouraged its use.  I think implementations that didn't give
you this mileage didn't get as much usage of it.  The nice generation of 
error messages wasn't required, but it was permitted; and it followed 
straightforwardly from the rules we gave about how to word your message.

> > No oneni X3J13

I'll assume this decodes to "No one in X3J13"

> > thought it
> > particularly important to get it right.  Sad, but I think true.

Well, as a strictly held universal quantification, I don't think this was
true.

I, for one, wanted it right.  But I'm pretty sure I was not alone.  
My recollection, FWIW [N.B.: I offer this only so there is more than one
point of view on the books, not because I assert my recollection to be 
'better' than Steve's in some objectively measurable way, but because I
think better journalism comes from more sources available for inspection],
was that the functional part of the condition system was believed by most
of us who were on the inner loop of this to be in pretty good shape, and
the part of the condition system that we had some interest in, but no budget
for, and had to leave "lacking" was more the issue of what functions signaled
what class of error under what circumstances, what restarts were available,
whether it was "must signal" or just "should signal" or "might signal".

I also seem to recall, though I never used the dialect personally, that 
the CMU compiler did lots of tricks with CHECK-TYPE where it tried to prove
the assertion and hence remove the test in situations where it knew it could
optimize it. That is, in the case of 

 (declaim (inline foo))
 (defun add2 (x) (check-type x integer) x)
 (add2 4)

it could optimize this to a constant 6 because 

 (check-type 4 integer)

had statically known effect.  Likewise:

 (dotimes (i 10)
   (check-type i fixnum)
   ...)

knew that 0 <= i < 10, and hence (fixnump i), and so could skip the test.

Consequently, the definition of CHECK-TYPE was not only not bankrupt, but
something of which extensive use of the stated definition had been made.
I'm pretty sure questions would have been asked if there had been a question.
They asked a lot of questions about much more subtle points.

> This is unfortunate.  I do use it in production code as a guard
> against invalid values.  Since it signals a correctable error,
> automatic handling can be put in place if necessary.  I don't
> anticipate using it in such a strange fashion with side-effects, but
> in the interest of correctness I bring this up.

I don't see why it shouldn't be predictable.

> The larger question, though, is how to write a macro like this which
> only evaluates /place/ once.  In my particular case, I am writing a
> macro which does coercion of values into types based on a table.  For
> example, if I expect a double-float, then I will try to convert the
> value into a double-float using one of a number of methods.  This is
> to satisfy the demands of ORBlink which expects precisely the correct
> type of value to be passed.
> 
> I can write this the simple way, where it transforms into basically:
>   `(setf ,place (%convert-fn ',type ,place))
> but I am interested in the correct way of doing this.

This sounds like something where you should be looking at writing a
setf expander, to make sure you get away with the fewest possible 
evaluations of subplaces.

I'm not sure how this relates to CHECK-TYPE, though.  It's certainly a
different question altogether.  Then again, I didn't read upthread.

I just got snagged on the assertion by steve that none of us cared
about a correct semantics.
From: Steven M. Haflich
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <UoFRd.1464$DC6.998@newssvr14.news.prodigy.com>
Kent M Pitman wrote:

>>"Steven M. Haflich" <·················@alum.mit.edu> writes:
>>>The specification of check-type in the ANS is poorly crafted and admits
>>>all sorts of unnecessary behavior and performance,
> 
> I don't know if I agree with smh here.

Kent, basically I agree with you.  (But watch out for the killer tweak
at the end of this message. :-)

The full paragraph on check-type is this:

  The first time place is evaluated, it is evaluated by normal evaluation
  rules. It is later evaluated as a place if the type check fails and the
  store-value restart is used; ...

Now, the most likely reading of this is that multiple evaluations might
occur _when_ the restart is _invoked_, but the sentence could be parsed

  (Subforms might be evaluated again if the type check fails) & (the
  restart is invoked)

where "used" might mean "established".  This is not the most-likely
reading, but is a plausible misreading of the obvious ANS intended
meaning.  The existence of ambiguity is sometimes ambiguous.

> (Incidentally, I don't mean to appear defensive.  I FREELY admit that
> his statement characterizes MANY of the wordings I chose elsewhere in
> the ANSI CL document.
...
  > I'd be curious what's actually a problem in this case, in practice.

None, because (as has already been pointed out) "check-type was in
practice never adopted as a reasonable and common thing to put in
production code."

However, if you want a _real_ ambiguity in the specification of
check-type, please review it and consider whether check-type ought
work with multiple values:

   (check-type (round 3 2) (values (fixnum fixnum)))

I see nothing in the ANS that suggests it should not work.  It is
obvious that the store-value function and restart are not capable of
handling multiple values, but the description of check-type

    check-type signals a correctable error of type type-error if the
    contents of place are not of the type typespec.

uses terms "contents of place" and "type" that elsewhere (as in the
definition of setf) would suggest support for type checking, if not
correction, of multiple values.  Probabkly no implementation
implements this...

> I just got snagged on the assertion by steve that none of us cared
> about a correct semantics.

Yeah, unfortunately there are a number of places in the language where
good intentions were not followed by popular adoption.  Myself, I
spent a good amount of time some years ago making setf and psetf and
all their cousins work properly with multiple values in ACL.  I am
unaware that anyone (other than myself) has ever seriously exploited
them.  (The following not carefully checked.)

cl-user(45): (define-setf-expander kons (cons)
	       (let ((obj (gensym))
		     (car (gensym))
		     (cdr (gensym))
		     (newcar (gensym))
		     (newcdr (gensym)))
		 (values (list obj car cdr)
			 (list cons `(car ,obj) `(cdr ,obj))
			 `(,newcar ,newcdr)
			 `(progn (setf (car ,obj) ,newcar
				       (cdr ,obj) ,newcdr)
				 (values ,newcar ,newcdr))
			 `(values ,car ,cdr))))
kons
cl-user(46): (setq *foo* (cons nil nil))
(nil)
cl-user(47): :ma (setf (kons *foo*) (round 3 2))
(let* ((#:g1000 *foo*) (#:g1001 (car #:g1000)) (#:g1002 (cdr #:g1000)))
   (multiple-value-bind (#:g1003 #:g1004)
       (round 3 2)
     (progn (setf (car #:g1000) #:g1003 (cdr #:g1000) #:g1004) (values #:g1003 #:g1004))))
cl-user(48): (setf (kons *foo*) (round 3 2))
2
-1
cl-user(49): *foo*
(2 . -1)

So should this work?  (check-type *foo* (values fixnum fixnum))
From: Kalle Olavi Niemitalo
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <87r7jc2uc4.fsf@Astalo.kon.iki.fi>
"Steven M. Haflich" <·················@alum.mit.edu> writes:

> uses terms "contents of place" and "type" that elsewhere (as in the
> definition of setf) would suggest support for type checking, if not
> correction, of multiple values.  Probabkly no implementation
> implements this...

I think, if this were really intended, there should also be a
variant of TYPEP that could conveniently test multiple values,
possibly passed as multiple arguments.

> (The following not carefully checked.)

Noted.  However, something in your code caught my eye, and I'd
like to know whether it was just a glitch or reflects a deeper
misunderstanding on either my or your part.

Specifically, your setf expander sets up two temporary variables
to hold the values of the place, i.e. the car and cdr of the
cons.  These variables are bound at the beginning of the
setf-like operation and do not change thereafter, and the
accessing form then just returns their values, even if the cons
has meanwhile been changed by the storing for or by something
else.  I think this is wrong and the accessing form should read
the values from the cons itself.  Otherwise, step 4 of section
5.1.3 would happen too early.

Here is how I would write the setf expander:

  (define-setf-expander car-and-cdr (cons)
    "Access the CAR and CDR of CONS as two values."
    (let ((obj (gensym))
          (newcar (gensym))
          (newcdr (gensym)))
      (values `(,obj)
              `(,cons)
              `(,newcar ,newcdr)
              `(values (setf (car ,obj) ,newcar)
                       (setf (cdr ,obj) ,newcdr))
              `(values (car ,obj)
                       (cdr ,obj)))))

(The excessive backquoting is meant to imply that callers cannot
expect these lists to be fresh or modifiable.)
From: Steven M. Haflich
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <zceSd.6041$DC6.5534@newssvr14.news.prodigy.com>
Kalle Olavi Niemitalo wrote:

> I think, if this were really intended, there should also be a
> variant of TYPEP that could conveniently test multiple values,
> possibly passed as multiple arguments.

Probably, except that it could not be a function.  It would have
to be a macro, and perhaps a rather ugly and inefficient one.

>>(The following not carefully checked.)
> 
> 
> Noted.  However, something in your code caught my eye, and I'd
> like to know whether it was just a glitch or reflects a deeper
> misunderstanding on either my or your part.
> 
> Specifically, your setf expander sets up two temporary variables
> to hold the values of the place, i.e. the car and cdr of the
> cons.  These variables are bound at the beginning of the
> setf-like operation and do not change thereafter, and the
> accessing form then just returns their values, even if the cons
> has meanwhile been changed by the storing for or by something
> else.  I think this is wrong and the accessing form should read
> the values from the cons itself.  Otherwise, step 4 of section
> 5.1.3 would happen too early.

I think you're right.  Good catch.  The only circumstance I can
see where this detail could make a difference is if this setf
expander were compounded with another expander that for some
reason used the getter a second time as aconvenient to return the
new values after performing the stores.  This possibility may be
a reason for this requierment in 5.1.1.2:

   The value returned by the accessing form is affected by execution
   of the storing form, but either of these forms might be evaluated
   any number of times.

>   (define-setf-expander car-and-cdr (cons)
>     "Access the CAR and CDR of CONS as two values."
>     (let ((obj (gensym))
>           (newcar (gensym))
>           (newcdr (gensym)))
>       (values `(,obj)
>               `(,cons)
>               `(,newcar ,newcdr)
>               `(values (setf (car ,obj) ,newcar)
>                        (setf (cdr ,obj) ,newcdr))
>               `(values (car ,obj)
>                        (cdr ,obj)))))
> 
> (The excessive backquoting is meant to imply that callers cannot
> expect these lists to be fresh or modifiable.)

I don't understand this last comment.  What is "excessive"?  In any
case, there is no possibility that any of these five backquote forms
could return any shared structure across successive calls.
From: Kalle Olavi Niemitalo
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <87u0o1g7cw.fsf@Astalo.kon.iki.fi>
"Steven M. Haflich" <·················@alum.mit.edu> writes:

> Kalle Olavi Niemitalo wrote:
>
>> I think, if this were really intended, there should also be a
>> variant of TYPEP that could conveniently test multiple values,
>> possibly passed as multiple arguments.
>
> Probably, except that it could not be a function.  It would have
> to be a macro, and perhaps a rather ugly and inefficient one.

It could be a function, if each value were passed as a separate
argument.  It would then be called like this:

  (multiple-value-call #'ugly-typep
      '(values integer real)    ; type specifier
      nil                       ; environment is not optional
    (floor number divisor))

That would surely be inefficient, though.

>> (The excessive backquoting is meant to imply that callers cannot
>> expect these lists to be fresh or modifiable.)
>
> I don't understand this last comment.  What is "excessive"?

I mean I used backquotes even in places where (list ...) would
have been simpler.

> In any case, there is no possibility that any of these five
> backquote forms could return any shared structure across
> successive calls.

I thought an implementation would be allowed to place the cons
generated by `(,(gensym)) in read-only storage, even though it
must be fresh.  However, I can't seem to find such a rule in
section 2.4.6 now.  Perhaps I misremember.
From: Kent M Pitman
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <uoeegqhrx.fsf@nhplace.com>
"Steven M. Haflich" <·················@alum.mit.edu> writes:

> [... Stuff that I (kmp) will reduce to smh asking if it's really so 
>  well-defined over multiple values elided somewhat...]
>
> So should this work?  (check-type *foo* (values fixnum fixnum))

First, let me cede that indeed I agree that CHECK-TYPE's definition should
have spoken to this issue, and so now I at least understand your objection.

I personally do not see any reason for 
 (check-type *foo* (values fixnum fixnum))
to work (by which I assume you mean "to be accepted as syntactically 
well-formed by a Lisp language processor that purports to conform", or
words to that effect), _even if_ *foo* is an ill-named symbol-macro that
nevertheless expands into the right number of values.

The reason I don't is not that there aren't 'places' in the language
that yield multiple vlaues, but that there aren't tools in the
language for dynamically checking multiple values appropriately.

TYPEP is defined to work on single values.  It doesn't work on the THE
type specifier, and I tend to assume (though again here I'll grant you
a weakness in the wording of the spec, since this is not anywhere
spelled out plainly) that when something says that x is "of type" y in
a context that is dynamically to be tested, that the ordinary semantics
of TYPEP should be the arbiter of such conformance.  It simply makes no
sense to me that there should be a secret and more powerful 
SYSTEM::TYPEP2 "special form" (and it would almost have to be a special
form) capable of computing the answer, and then that we would not bother
to publish such power, since it would be essential to any user writing
similar code.

To do the operation you propose, without such a special form, would at 
minimum require an expansion like:

 (flet ((check-type-trampoline (&optional (value1 nil value1-p)
                                          (value2 nil value2-p)
                                &rest more-values)
          (declare (dynamic-extent more-values))
          (loop
            (if (or (not value1-p) (not (typep value1 'fixnum))
                    (not value2-p) (not (typep value2 'fixnum)))
                (restart-case (error 'type-error ...)
                  ...)
                (apply #'values value1 value2 more-values)))))
   (declare (dynamic-extent #'check-type-trampoline))
   (multiple-value-call #'check-type-trampoline *foo*))

[where check-type-trampoline would presumably be a gensym, but I didn't
 make it so in order to make the code clearer here]

I'm not even sure all implementations can compile this kind of 
MULTIPLE-VALUE-CALL efficiently, or whether I'd think an inefficient
call at this point counted as ordinary evaluation.  Most users fuss 
over the mere computational overhead of doing a type test at all,
without worrying that the system might be introducing computational
overhead like inefficient closure and call stack management on top of
things.  I think that most people naively (and correctly) expect the
following from check-type:

  (unless (typep *foo* '...whatever...)
    ... something more complicated and corrective involving *foo* ...)

But I do agree that there's some wiggle room for you to say that a few 
additional words were in order to assure this.
From: Steven M. Haflich
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <b_eSd.2303$OU1.691@newssvr21.news.prodigy.com>
Kent -- I'm continuing this thread mostly for the enjoyment of its
language lawyer aspects, not because it is important, nor because I
think there are huge flaws in the ANS.  There _are_ flaws concerning
multiple values in the ANS, but they are not in the drafting.  They
are rather in the history and lack of thoroughness in integrating
m-v into the language.  When changes were considered to the language,
m-v semantics was generally item 7 on the list of 3 important
aspects to consider.

Kent M Pitman wrote:

> "Steven M. Haflich" <·················@alum.mit.edu> writes:
> 
> 
>>[... Stuff that I (kmp) will reduce to smh asking if it's really so 
>> well-defined over multiple values elided somewhat...]
>>
>>So should this work?  (check-type *foo* (values fixnum fixnum))
> 
> 
> First, let me cede that indeed I agree that CHECK-TYPE's definition should
> have spoken to this issue, and so now I at least understand your objection.
> 
> I personally do not see any reason for 
>  (check-type *foo* (values fixnum fixnum))
> to work (by which I assume you mean "to be accepted as syntactically 
> well-formed by a Lisp language processor that purports to conform", or
> words to that effect), _even if_ *foo* is an ill-named symbol-macro that
> nevertheless expands into the right number of values.

My use of a variable in a place that wants to exemplify multiple was
just a braino.  Sorry.  I intended something like:

  (check-type (kons *foo*) (values fixnum fixnum))

ss in the immediately-preceding examples where the m-v-setfable kons
function had been defined.

> The reason I don't is not that there aren't 'places' in the language
> that yield multiple vlaues, but that there aren't tools in the
> language for dynamically checking multiple values appropriately.
> 
> TYPEP is defined to work on single values.  It doesn't work on the THE
> type specifier, and I tend to assume (though again here I'll grant you

I believe there is a different reason that the values type is not accepted
by the the special form.  It logically _should_ be, since the annotates
values that flow within a computation, and values flowing through a
calculation are in general multiple values.

I think the _real_ reason values is not accepted by the is because we on
X3J13 were too wimpy to insist that the compiler writers do the work to
make it work, and the compiler writers were too wimpy to want to make it
work.  This situation was compounded, of course, by the fact that these
two groups of wimps were largely the same wimps.  But in retrospect, I
see no justification for this limitation.

By the way, I remember a discussion at on eof the committee meetings
about the meaning of a values ftype declaration in the circumstance of
too many or too few values.  I forget what we decided, but I remember
clearly that we mostly felt that we hadn't thought through the issue
deeply enough to understand the implications.

> a weakness in the wording of the spec, since this is not anywhere

Again, I exonerate you as editor; the problem is not in the drafting
but in the history and design behind it.

> spelled out plainly) that when something says that x is "of type" y in
> a context that is dynamically to be tested, that the ordinary semantics
> of TYPEP should be the arbiter of such conformance.  It simply makes no
> sense to me that there should be a secret and more powerful 
> SYSTEM::TYPEP2 "special form" (and it would almost have to be a special
> form) capable of computing the answer, and then that we would not bother
> to publish such power, since it would be essential to any user writing
> similar code.
> 
> To do the operation you propose, without such a special form, would at 
> minimum require an expansion like: ...

I think it could be written as a portable macro.  The expansion would
capture the multiple-values using multiple-value-list, do the type
checks on the individual values, then return them outward using
values-list.  This is horrendously inefficient, of course, but an
implementation could back it up with a special form that directly
accesses the internal object that carries multiple values, avoiding
the extra consing and unconsing.  This would be straightforward to
implement in the architecture of ACL, at least.

It would also been necessary to change the store-value function and
restart to handle multiple values.  This, too, is inelegant, since if
the store-value function is to remain a function it would have to
accept the multiple values as a &rest list of individual values, and
there is already the &optional condition argument.

This leads directly to the next running-dog revisionist m-v langauge
design question:  Should the restart mechanism have been designed to
support passing and receiving  multiple values via invoke-restart,
etc.?

I will not feel slighted if no one bothers to respond to this
question...
From: Kent M Pitman
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <uu0o2dod5.fsf@nhplace.com>
"Steven M. Haflich" <·················@alum.mit.edu> writes:

> Kent M Pitman wrote:
> 
> > The reason I don't is not that there aren't 'places' in the language
> > that yield multiple vlaues, but that there aren't tools in the
> > language for dynamically checking multiple values appropriately.
> > TYPEP is defined to work on single values.  It doesn't work on the
> > THE
> > type specifier, and I tend to assume (though again here I'll grant you
> 
> I believe there is a different reason that the values type is not accepted
> by the the special form.  It logically _should_ be, since the annotates
> values that flow within a computation, and values flowing through a
> calculation are in general multiple values.

Yeah, that's a perfectly consistent point of view.

The reason I don't have this point of view is that it would argue we should
have included a TYPEP variant that was a special form, in order to allow 
people to do
 (MULTIPLE-VALUE-TYPEP ; in the spirit of MULTIPLE-VALUE-PROG1
        (FOO)
   (VALUES FIXNUM FLOAT))
since there's no reason not to.  That is, if the mechanism was needed in 
CHECK-TYPE (and others?), it should have been offered in primitive form as
well so that people could use the building block in other ways.

Since it's not offered as a building block, I personally take that as an
admission that probably the operator is not present internally at all.

Incidentally, the same is the reason that I went along with the relatively
baroque pretty printer spec for CL.  One might argue the language would be
smaller without it, but once we had *PRINT-PRETTY* and a few of the other
bells and whistles related to printing, it was required to be inside, so 
why not expose it so that others could use it and we could get more mileage
out of it?

> I think the _real_ reason values is not accepted by the is because we on
> X3J13 were too wimpy to insist that the compiler writers do the work to
> make it work, and the compiler writers were too wimpy to want to make it
> work.  This situation was compounded, of course, by the fact that these
> two groups of wimps were largely the same wimps.  But in retrospect, I
> see no justification for this limitation.

Probably I agree with this analysis, though I usually express it quite 
differently:  Good design groups should have representatives of both the
user community and the compiler community, in part because sometimes users
ask for things that are hard and the compiler community may push back and
say "oh, please, don't make us do that work--do you really need that?" and
users need to sometimes say "yes" (though sometimes I think they might 
reasonably back down if they didn't realize the cost).  Having both 
communities keeps the process honest.  

X3J13 began, as I recall, with about 60 attendees from several dozen
organizations (many sending more than 1 attendee, including the
allowed member and alternate plus even some additional more
questionable attendees).  But only a small number of the attendees
were implementors.  Many were users.  Unfortunately, as ANSI had
warned us, the REAL cost of the process is not the dues, but the
travel costs, and as the years wore away, the compiler designers were
mostly EQ to the people with a fixed business interest in Lisp.  The
"users" were often groups that could afford to change to another
language if they had to, and so they were willing to stop sending
representatives and just play "wait and see" about the language.  By
the end, we had meetings with usually about 6-7 organizations
represented, of which almost all were vendors and a couple were
"dedicated inviduals" (Kim Barrett and Sandra Loosemore, I think, not
sure if anyone else was in that camp at that time) who themselves had
been compiler writers at some time or another but whose present
employers weren't funding them.  We had lost all the user
representation on the committee.

As a consequence, there was no one to bully the X3J13 committee to do
anything hard, I'll allege. (Yes, yes, I'm overgeneralizing, and even
somewhat unfairly.  Vendors were tired and their design budget was
depleted.  In some ways, we can call this wimpy.  HOWEVER, in their
defense, and in the defense of such standards efforts generally, or
even of the free market more generally, "running out of money" is a
"forcing function" that causes people to make the really important
fish-or-cut-bait decisions they really have to make to finally finish
a standard.  ISO went through a similar thing, with people all over
the map about ISLISP, until we were threatened with shutdown by ISO,
and then finally people said "oh, I see, agreement/convergence is not
optional" and things went more smoothly.  But what's peculiar about
the situation with X3J13 was that the people were "happy to agree" because
they were all vendors effectively agreeing to "not provide x feature 
because it would be expensive to".  And, as I say, we had to get the
standard out, so that's good.  But a few users around to say "no, this
matters and must get in" would have gotten us DEFSYSTEM, I bet, and 
probably COMPILE-FLAVOR-METHODS (with some better name as it was cast
from Flavors to CLOS), and maybe a few other last-minute things that failed.

So yes, we were often wimps at the end.  But in part what made us
wimps was that the should-have-been natural wimp-enemies (users) had
wimped out even worse and were not even present there to keep us
honest.  I don't think it made a mess of the standard.  But I do think
there are things the users didn't get where the answer is "you have
only yourself to blame".

I don't know that I think multiple value type discrimination was above
the radar on this.  (Whether that confirms or contradicts your point is
not clear to me, since I don't know if it had been above the radar whether
we would have expressed more sense of will or would have said "we don't
care, and that's why it never made it above the radar, so send it back down
in priority". :)

> By the way, I remember a discussion at on eof the committee meetings
> about the meaning of a values ftype declaration in the circumstance of
> too many or too few values.  I forget what we decided, but I remember
> clearly that we mostly felt that we hadn't thought through the issue
> deeply enough to understand the implications.

Well, I never saw this discussion, but I have a meta-rule about such things
that says when one person sees something and one person doesn't, probably
the one who saw it is right.  No one is omniscient nor even omnipresent.
Things like this probably did happen all the time.  Whether they can then
rightly be aggregated as a statement about the group as a whole is harder
to say, but at least I see how you're getting to your position.  Thanks.

> [discussion of SYSTEM::TYPEP2, which I've above renamed as 
>  MULTIPLE-VALUE-TYPEP]
>
> I think it could be written as a portable macro.  The expansion would
> capture the multiple-values using multiple-value-list, do the type
> checks on the individual values, then return them outward using
> values-list.  This is horrendously inefficient, of course, but an
> implementation could back it up with a special form that directly
> accesses the internal object that carries multiple values, avoiding
> the extra consing and unconsing.  This would be straightforward to
> implement in the architecture of ACL, at least.

Yeah, probably.  Then again, could still be...

> It would also been necessary to change the store-value function and
> restart to handle multiple values.  This, too, is inelegant, since if
> the store-value function is to remain a function it would have to
> accept the multiple values as a &rest list of individual values, and
> there is already the &optional condition argument.
> 
> This leads directly to the next running-dog revisionist m-v langauge
> design question:  Should the restart mechanism have been designed to
> support passing and receiving  multiple values via invoke-restart,
> etc.?

Well, here I'll appeal to "current practice", as standards are supposed to
appeal to that when possible.

The current practice was a lot similar to what we standardized on.
What you're talking about is "design".  And standards groups are bad forums
for design work.

Besides, I was within a few characters of being done responding.  So I'm
going to, as you so elegantly put it, "wimp out" here.
 
> I will not feel slighted if no one bothers to respond to this
> question...

Too late.  If you want this kind of treatment, you have to say so 
higher up in your message for those of us who reply as we read.  ;)
From: Kalle Olavi Niemitalo
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <87r7j5g67n.fsf@Astalo.kon.iki.fi>
"Steven M. Haflich" <·················@alum.mit.edu> writes:

> I believe there is a different reason that the values type is not accepted
> by the the special form.  It logically _should_ be, since the annotates
> values that flow within a computation, and values flowing through a
> calculation are in general multiple values.

The THE special form does accept the VALUES type specifier; that
is spelled out quite clearly.  Perhaps you meant CHECK-TYPE, but
I don't see how that annotates values.

(About a CHECK-TYPE that supports multiple values:)
> I think it could be written as a portable macro.  The expansion would
> capture the multiple-values using multiple-value-list, do the type
> checks on the individual values, then return them outward using
> values-list.

Currently, CHECK-TYPE always returns NIL, so the extended version
would only have to capture those values that it actually checks.
If (check-type (foo) (values t &optional)) must check the number
of values [1], the expansion could do it in this way:

  (multiple-value-call #'(lambda (#:g1 &optional (#:g2 nil #:g3)
                                  &rest #:g4)
                           (declare (ignore #:g2 #:g4))
                           (unless (and (typep #:g1 t) (not #:g3))
                             ...))
    (foo))

I expect this would give the compiler enough information to
optimize out collecting the extra values to the &rest list.

References:

[1] Issue THE-VALUES
http://www.cliki.net/Issue%20THE-VALUES
From: Kent M Pitman
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <usm3lgqwh.fsf@nhplace.com>
Kalle Olavi Niemitalo <···@iki.fi> writes:

> Currently, CHECK-TYPE always returns NIL, so the extended version
> would only have to capture those values that it actually checks.
> If (check-type (foo) (values t &optional)) must check the number
> of values [1], the expansion could do it in this way:
> 
>   (multiple-value-call #'(lambda (#:g1 &optional (#:g2 nil #:g3)
>                                   &rest #:g4)
>                            (declare (ignore #:g2 #:g4))
>                            (unless (and (typep #:g1 t) (not #:g3))
>                              ...))
>     (foo))

No, I don't think  you can do this unless you want to blow out in high
safety mode in some implementations for (foo) returning (values).
You must always make all args optional and check if they were there,
even to require them, if you want to be running your own error messages
and not the system's.
From: Kalle Olavi Niemitalo
Subject: Re: ANSI compliance of check-type in various lisps
Date: 
Message-ID: <87ll9df8zh.fsf@Astalo.kon.iki.fi>
Kent M Pitman <······@nhplace.com> writes:

> No, I don't think  you can do this unless you want to blow out in high
> safety mode in some implementations for (foo) returning (values).

Oops.  I was thinking of too many values and forgot about too few.