From: Henrik Motakef
Subject: Evaluation order in structure constructors
Date: 
Message-ID: <87vg0d2ew1.fsf@interim.henrik-motakef.de>
Hi,

I wonder if it's guaranteed that the arguments given to a structure
constructor are evaluated in order, i.e.

  (defvar *foo* 0)
  (defstruct bar a b)
  (make-bar :a (incf *foo*) :b (incf *foo*))

will always give

  #S(BAR :A 1 :B 2)

I guess so, because according to the hyperspec, "[defstruct] defines a
constructor /function/ named make-constuctor-name" (emphasis mine), so
I'd expect standard evaluation order. Or is it legal for a constructor
to be, say,  a macro doing funny things with it's arguments?

What made me suspicious was that the spec explicitly notes that "all
the keywords and forms are evaluated", which would be redundant if it
had to be a vanilla function.

tia
Henrik
From: Steven M. Haflich
Subject: Re: Evaluation order in structure constructors
Date: 
Message-ID: <3E334823.8070505@alum.mit.edu>
Henrik Motakef wrote:

> I wonder if it's guaranteed that the arguments given to a structure
> constructor are evaluated in order, i.e.
> I guess so, because according to the hyperspec, "[defstruct] defines a
> constructor /function/ named make-constuctor-name" (emphasis mine), so
> I'd expect standard evaluation order. Or is it legal for a constructor
> to be, say,  a macro doing funny things with it's arguments?
> 
> What made me suspicious was that the spec explicitly notes that "all
> the keywords and forms are evaluated", which would be redundant if it
> had to be a vanilla function.

Redundancy may be true, but neither is it false.

Since the ANS specifies that the constructor is a function, it must be
a function.  A function can be passed as a first-class object and/or
funcalled; a macro cannot.  A global operator cannot simultaneously
be defined as both a function and a macro.  Normal functional argument
evaluation is required, but this is not so much a property of the
function itself rather due to the semantics that argument evaluation
is done by the +calling_ function body, not the _called_ function.
This wasn't always the case in prehistoric Lisps, but is essentially
a logical requirement of modern lexically-scoped languages.  Think about
who does the argument evaluation, and why.

If you want to learn more about prehistoric, non-lexically-scoped Lisp
dialects, please see my very brief explanatory page:

   <http://www.franz.com/~smh/nlambda.html>

Despite the fact that a constructor cannot be defined as a macro, many
implementations may optimize calls to the constructor, at least if all
the keyword arguments are manifestly visible constants.  This
optimization can be important to performance.  In effect, it is as if
the call is replaced by an inlined call to an automatically-generated
boa constructor.

Given that the compiler may inline constructor calls (if not declared
notinline) there remains a question whether the implementation may define
a compiler macro as the mechanism for doing so.  SFAIK, nithing in the
ANS addresses this directly.  The problem is that the implementation
defining such compiler macro would conflict with the user defining a
compiler macro, and nothing in the ANS implies the user is not free to
establish such a definition.  (BTW, defstruct predates compiler macros
by more than a decade, and at the timecompiler macros were added there
was resistance to messing with the historical defstruct mechanism in any
way; indeed, there was sentiment instead for deprecating it.)

I consider this to be something theoretically worthy of cleanup in the
ANS, but objectively not very important, since a compiler macro provided
byt he implementation and a compiler macro provided by the user would
have the same intention and the same effect, and both would be required
by language conventions to implement exactly the same behavior as the
functional constructor.  The implementation compiler macro might have
an advantage in knowing the efficiency details of the implementation, but
the user compiler macro might have an advantage of knowing something about
the nature of typical calls within the application.