CLHS: Issue FUNCTION-NAME Writeup

#

## Issue FUNCTION-NAME Writeup

    Status: Proposal LARGE, with sections 7, 8, 9 removed, passed Mar 89 X3J13

    Issue:         FUNCTION-NAME

    References:    SETF rules for what -place- can be (pp.94-7)
                   FBOUNDP function (p.90)
                   FMAKUNBOUND function (p.92)
                   FUNCTION special form (p.87)
                   SYMBOL-FUNCTION and setf of symbol-function (p.90)
                   88-002R pages 1-21, 2-21, 2-26, 2-39, 2-44, 2-46, 2-51, and 2-55
                   (There are additional references for the MEDIUM and LARGE
                    proposals, but they are not listed here.  They're obvious.)

    Related issues: SETF-FUNCTION-VS-MACRO, SETF-PLACES (both subsumed by this)

    Category:      ADDITION

    Edit history:  Version 1, 23-Jan-89, by Moon
                                  (based on discussion at Jan X3J13 meeting)

    Problem description:

    The Common Lisp Object System needs a well-defined way to relate the name
    and arguments of a writer function to those of a reader function, because
    both functions can be generic and can have user-defined methods.  The way
    that was adopted into Common Lisp when X3J13 voted to accept document
    88-002R was to use a list (SETF reader) as the name of the writer function.

    Some changes to the non-object-oriented portion of Common Lisp are required
    in order to support this.

    This issue has three proposals.

    Proposal (FUNCTION-NAME:SMALL):

      Add a new concept "function-name" (called "function-specifier" in
      88-002R).  A function-name is either a symbol or a 2-element list whose
      first element is the symbol SETF and whose second element is a symbol.
      Implementations are free to extend the syntax of function-names to
      include lists beginning with additional symbols other than SETF.

      Add a new function (FDEFINITION function-name), which returns the
      current global function definition named by function-name, or signals
      an error if there is no global function definition.  This follows all
      the same rules listed for SYMBOL-FUNCTION in CLtL p.90.

      Add SETF of FDEFINITION to change the current global function definition
      named by a function-name.  This follows all the same rules listed for
      SETF of SYMBOL-FUNCTION in CLtL p.90.

      Change the FBOUNDP and FMAKUNBOUND functions, and the FUNCTION special
      form, to accept function-names in place of symbols.  Implementation
      defined extensions to the syntax of function-names cannot use the
      symbol LAMBDA, since FUNCTION already uses that symbol.

      Change the rules for SETF places (CLtL pp.94-7) by adding the following
      clause after all the existing clauses:

       - Any other list whose first element is a symbol, call it reader.
         In this case, SETF expands into a call to the function named by the
         list (SETF reader).  The first argument is the new value and the
         remaining arguments are the values of the remaining elements of
         -place-.  This expansion occurs regardless of whether reader or
         (SETF reader) is defined as a function locally, globally, or not at
         all.  For example,
             (SETF (reader arg1 arg2...) new-value)
         expands into a form with the same effect and value as
             (LET ((#:temp-1 arg1)          ;force correct order of evaluation
                   (#:temp-2 arg2)
                   ...
                   (#:temp-0 new-value))
               (FUNCALL (FUNCTION (SETF reader)) #:temp-0 #:temp-1 #:temp-2...)).

      Change the functions GET-SETF-METHOD and GET-SETF-METHOD-MULTIPLE-VALUE
      to implement the above change to the rules.

      Document that a function named (SETF reader) should return its first
      argument as its only value, in order to preserve the semantics of SETF.

      Change the macro DEFGENERIC and the function ENSURE-GENERIC-FUNCTION to
      refer to the function FDEFINITION where they now refer to the function
      SYMBOL-FUNCTION.

      Change the macros DEFCLASS, DEFGENERIC, and DEFMETHOD, the special forms
      GENERIC-FLET and GENERIC-LABELS, and the functions DOCUMENTATION and
      ENSURE-GENERIC-FUNCTION to use the term "function-name" where they now
      use the term "function-specifier" or "function specifier".

    Rationale for FUNCTION-NAME:SMALL:

      This is the minimum change to Common Lisp needed to do what 88-002R says
      about (SETF reader).  Giving implementations freedom to extend the syntax
      of function-names allows for current practice.  Changing the name from
      "function-specifier" to "function-name" avoids confusion and improves
      consistency with the rest of the language, at the cost of a few small
      changes to 88-002R.

    Proposal (FUNCTION-NAME:MEDIUM):

      Everything in FUNCTION-NAME:SMALL, and in addition:

      Change the DEFUN macro to accept a function-name for its name argument,
      instead of only accepting a symbol.  If function-name is (SETF sym),
      the body is surrounded by an implicit block named sym.

    Rationale for FUNCTION-NAME:MEDIUM:

      Keeping DEFUN consistent with DEFMETHOD is a good idea.  Also 88-002R
      says "The name of a generic function, like the name of an ordinary
      function, can be either a symbol or a two-element list whose...", which
      implies this change to DEFUN.

    Proposal (FUNCTION-NAME:LARGE):

      Everything in FUNCTION-NAME:MEDIUM, and in addition the following
      numbered points, each of which could be adopted independently,
      except where explicitly noted:

      1. Change the function COMPILE to accept a function-name as its name
      argument.

      2. Change the function DISASSEMBLE to accept a function-name as its name
      argument.

      3. Change the FTYPE, INLINE, and NOTINLINE declarations and proclamations
      to accept function-names, not just symbols, as function names.

      4. Change the FLET and LABELS special forms to accept a function-name in
      the name position, not just a symbol.

      5. Change the TRACE and UNTRACE macros to accept function-names, not just
      symbols, in the function name positions.

      6. Change the ED function to accept (ED function-name) in place of
      (ED symbol).

      7. Change the syntax of a function call to allow a function-name as the
      first element of the list, rather than allowing only a symbol.

      8. Change the DEFMACRO macro and the MACROLET special form to accept a
      function-name in the name position, not just a symbol.  Change the
      MACRO-FUNCTION function to accept function-names, not just symbols.
      Change the last rule for SETF places to use
        ((SETF reader) #:temp-0 #:temp-1 #:temp-2...)
      in place of
        (FUNCALL (FUNCTION (SETF reader)) #:temp-0 #:temp-1 #:temp-2...)
      so that (SETF reader) can be defined as a macro.  This depends on item
      7.  If item 4 is rejected, MACROLET should be stricken from this item.

      9. Add an optional environment argument to FDEFINITION, SETF of
      FDEFINITION, FBOUNDP, and FMAKUNBOUND.  This is the same as the
      &environment argument to a macroexpander.  This argument can be used to
      access local function definitions, to access function definitions in the
      compile-time remote environment, and to modify function definitions in
      the compile-time remote environment.

      10. Change the second, third, fourth, fifth, seventh, and ninth rules for
      SETF places so that they only apply when the function-name refers to the
      global function definition, rather than a locally defined function or
      macro.  (The ninth rule is the one that refers to DEFSETF and
      DEFINE-SETF-METHOD; the other rules listed are the ones that list
      specific built-in functions).  The effect of this change is that SETF
      methods defined for global functions are ignored when there is a local
      function binding; instead, the function named (SETF reader), which may
      have a local function binding, is called.  This change is most useful
      in connection with item 4, but does not actually depend on it.

      11. Clarify that the eighth rule for SETF places (the one for macros)
      uses MACROEXPAND-1, not MACROEXPAND.

    Rationale for FUNCTION-NAME:LARGE:

      This extends the new feature throughout the language, in order to make
      things generally more consistent and powerful.  Point by point:

      1,2,3 - one should be able to compile, examine, and make declarations
      about functions regardless of whether they are named with symbols or
      with lists.

      4 - locally defined non-generic SETF functions are a logical companion
      to locally defined generic SETF functions, which can be defined with
      GENERIC-FLET or GENERIC-LABELS.  They make sense on their own, since one
      might define a local reader function and want a local writer function
      to go with it.

      5,6 - one should be able to apply development tools to functions
      regardless of how they are named.  The function DOCUMENTATION was already
      updated to work for function-names by 88-002R.  There might be some
      difficulty with implementation-dependent syntax extensions to TRACE and
      UNTRACE conflicting with this new syntax.

      7 - this restores consistency between the FUNCTION special form and the
      first element of a function call form.

      8 - it seems more consistent to allow macros to be named the same way
      that ordinary functions are named.  However, this might be considered
      redundant with DEFSETF.

      9 - this is not needed by the "chapter 1 and 2" level of CLOS, but might
      be used by the metaobject based implementation of ENSURE-GENERIC-FUNCTION.

      10 - this change was in SETF-FUNCTION-VS-MACRO and makes item 4 more useful.

      11 - this change was in SETF-FUNCTION-VS-MACRO and is a good idea, but
      actually is independent of everything else being proposed here.

    Examples:

    ;This is an example of the sort of syntax 88-002R allows
    (defmethod (setf child) (new-value (parent some-class))
      (setf (slot-value 'child parent) new-value)
      (update-dependencies parent)
      new-value)
    (setf (child foo) bar)

    ;If SETF of SUBSEQ was not already built into Common Lisp,
    ;it could have been defined like this, if the MEDIUM or LARGE
    ;proposal is adopted.
    (defun (setf subseq) (new-value sequence start &optional end)
      (unless end (setq end (length sequence)))
      (setq end (min end (+ start (length new-value))))
      (do ((i start (1+ i))
           (j 0 (1+ j)))
          ((= i end) new-value)
        (setf (elt sequence i) (elt new-value j))))

    ;The preceding example would have to be defined like this
    ;if only the SMALL proposal is adopted.  This is a method
    ;all of whose parameter specializer names are T.
    (defmethod (setf subseq) (new-value sequence start &optional end)
      (unless end (setq end (length sequence)))
      (setq end (min end (+ start (length new-value))))
      (do ((i start (1+ i))
           (j 0 (1+ j)))
          ((= i end) new-value)
        (setf (elt sequence i) (elt new-value j))))

    ;Another example, showing a locally defined setf function
    (defun frobulate (mumble)
      (let ((table (mumble-table mumble)))
        (flet ((foo (x)
                 (gethash x table))
               ((setf foo) (new x)
                 (setf (gethash x table) new)))
          ..
          (foo a)
          ..
          (setf (foo a) b))))

    ;get-setf-method could implement setf functions by calling
    ;this function when the earlier rules do not apply
    (defun get-setf-method-for-setf-function (form)
      (let ((new-value (gensym))
    	(temp-vars (do ((a (cdr form) (cdr a))
    			(v nil (cons (gensym) v)))
    		       ((null a) v))))
        (values temp-vars
    	    (cdr form)
    	    (list new-value)
    	    `(funcall #'(setf ,(car form)) ,new-value ,@temp-vars)
    	    `(,(car form) ,@temp-vars))))

    Current practice:

      No implementation supports exactly what is proposed.  Symbolics Genera
      and the TI Explorer support something close to the MEDIUM proposal, but
      differing in a number of details.  Symbolics Genera supports items 1, 2,
      3, 6, and 11, and modified forms of items 5 and 8, of the LARGE proposal.
      Moon considers this proposal's variations from Symbolics current practice
      to be an improvement, although incompatible in some cases.

      Many implementations currently support only symbols as function names.

      Symbolics Genera and the TI Explorer have some additional function-name
      syntaxes.

    Cost to Implementors:

      The SMALL and MEDIUM proposals are estimated to be no more than 50 lines
      of code and require no changes to the "guts" of the interpreter and
      compiler.  Most of the code for this can be written portably and was
      shown on two slides at the X3J13 meeting.

      Some of the changes in the LARGE proposal are trivial, some require
      the compiler to use EQUAL instead of EQ to compare function names, and
      items 4, 7, and 8 might require a more substantial implementation
      effort.  Even that effort is estimated to be negligible compared to
      the effort required to implement CLOS.

    Cost to Users:

      No cost to users, other than program-understanding programs, since this
      is an upward compatible addition.

      As with any language extension, some program-understanding programs may
      need to be enhanced.  A particular issue here is programs that assume
      that all function names are symbols.  They may use GET to access
      properties of a function name or use EQ or EQL (perhaps via MEMBER or
      ASSOC) to compare function names for equality.  Such programs will need
      improvement before they can understand programs that use the new feature,
      but otherwise they will still work.

    Cost of non-adoption:

      We would have to make some other language change since the language
      became inconsistent when 88-002R was adopted.

    Performance impact:

      This has no effect on performance of compiled code.  It might slow
      down the compiler and interpreter but not by very much.

    Benefits:

      CLOS will work as designed.

    Esthetics:

      Some people dislike using anything but symbols to name functions.
      Other people would prefer that if the change is to be made at all,
      the LARGE proposal be adopted so that the language is uniform in its
      treatment of the new extended function names.  Other proposals for
      how to deal with SETF in CLOS were considerably less esthetic,
      especially when package problems are taken into account.

      SETF would be more esthetic, but less powerful, if it had only the
      proposed setf functions and did not have setf macros.  Such a major
      incompatible change is of course out of the question; however, if setf
      functions are stressed over setf macros, SETF will be much easier to
      teach.

    Discussion:

      Moon supports at least FUNCTION-NAME:MEDIUM.  He does not necessarily
      approve of all parts of FUNCTION-NAME:LARGE.

    !
    Additional Comments:

    On the whole, I like this presentation much better than either of the
    other two writeups that were circulated previously.  I suspect that it
    might be necessary to vote on each of the items in the LARGE proposal
    individually, though.  I think I would support items 1, 2, and 11, and
    don't have any particular objections to 3, 5, and 6.  For item 4, if
    consistency with GENERIC-FLET and GENERIC-LABELS is an object, another
    alternative is to change those two special forms to be like ordinary
    FLET and LABELS, instead of vice versa.

    - - - - - - -
    I support FUNCTION-NAME:MEDIUM and may support LARGE once I think about
    it some more.

    As I explained in Hawaii, support for either of these is based on the
    :conc-name bugs being removed from the condition system.  Of course, I
    believe the best way to do that is to CLOSify it.
    - - - - - - - -
    I'm still thinking about this, but while I am I wanted point out that
    MEDIUM is unacceptable to me because I don't think FLET and DEFUN should
    disagree on what they permit as defined names. If FLET were added to
    MEDIUM, I suspect I'd think it was an internally consistent position.

    LARGE has an appeal to me in general, but I'm still mulling over
    the specifics.
    - - - - - - - - - -
    I favor the FUNCTION-NAME:LARGE proposal, because it defines a single,
    useful notion of what a function name is.  The other proposals have
    the flaw that there are two kinds of function names:  symbols, and
    extended names, with only some of the Lisp primitives accepting the
    latter.  This may be convenient for some implementations, for the
    short term, but it fragments the language.

    I have two other comments on the proposal.

    A. Reducing the Cost to Implementors

    One observation you could put in the Cost To Implementors section is
    that none of the SMALL, MEDIUM, or LARGE proposals require changes to
    the "guts" of the interpreter and compiler.  This is because an
    implementation is free to use plain symbols internally to name
    functions, and use a hack like JonL's SETF:|3.FOO.BAR| mapping to
    convert non-symbol names to symbols.  This conversion would be done as a
    part of parsing the handful of forms which accept function names, and
    then all other passes of the interpreter and compiler (the "guts") would
    just see symbols.  (By "parsing" I mean ensuring the right number and
    type of syntactic subforms.  You can see that this is a very early and
    simple stage of processing.)  Or, Lisp compilers with an "alphatization"
    phase could perform function name symbolization at that phase.

    B. Finishing the Job of Regularization

    I'd like to suggest two additions to your smorgasbord of options in the
    FUNCTION-NAME:LARGE section of the proposal.  One addition would
    regularize a major special case of functions--lambda expressions.  The
    other addition would reaffirm an unstated regularity in the language,
    that function names can stand in for functions under FUNCALL and APPLY.
    Not only can the treatment of symbolic and setf-list function names be
    regularized, but lambda too can be treated in a consistent manner.

    If these two points are added to your proposal, the language as a whole
    would have a completely uniform treatment of functions and function
    names.  Here they are:

    13. Declare that any function name is a suitable argument to FUNCALL and
        APPLY.  In such a case, the function name is passed to FDEFINITION,
        and the result (which may in turn be a function name) is called.
        That is, the following two expressions are equivalent, when fname
        is a function name:
    	(FUNCALL fname x y)
    	  <==>
    	(FUNCALL (FDEFINITION fname) x y)
        Note that the definition is sought in the global environment.
        Compare with the rule which applies to a function name occurs,
        syntactically, as the car of a list in code:
    	(fname x y)
    	  <==>
    	(FUNCALL (FUNCTION fname) x y)
    	  <==> (under proposal item 9)
    	(FUNCALL (FDEFINITION fname <local-environment>) x y)

    12. Declare that any lamba expression (i.e., a list whose car is LAMBDA and
        whose cdr is a well-formed lambda argument list and body) is a function
        name.  The effects of the function name accessors on lambda expressions
        are as follows.  FDEFINITION returns an implementation-defined value which
        is the function specified the lambda expression, closed in the global
        environment.  This FDEFINITION value cannot be changed by SETF.
        FBOUNDP always returns T, and MAKUNBOUND is an error.

    Esthetics:

    The effect of items 11 and 12 is to complete the regularization of
    Common Lisp's treatment of functions and function names.  The total
    effect of proposal items 1 through 12 is that Lisp has just two notions
    for referencing function objects: FUNCTIONS, which are Lisp objects that
    directly represent executable code, and FUNCTION NAMES, which can denote
    functions.  Symbols, SETF function names, and lambda expressions are all
    examples of the latter notion.  The former notion is highly
    implementation dependent.  Function names can occur as syntactic
    entities in code.  FUNCALL and APPLY work uniformly on both functions
    and function names, with a consistent semantics.

    Lambda expressions are often thought to denote "anonymous" functions, so
    it may seem paradoxical to treat them as names.  The paradox is only
    apparent, since the expression itself has the properties of a Lisp
    function name: It is (typically) a cons tree which can be read, printed,
    and stored in source files, and it denotes a well-defined Lisp function.

    Benefit to Users:

    Function names are useful for representing objects in remote
    environments, because they need not be bound at all times to the same
    function, or to any function, and because they are typically stable in
    meaning across reads and prints, where plain functions are not.
    Programs which deal simultaneously with remote and local environments,
    such as CLOS, can probably be simplified, since function names
    can be used uniformly, rather than an ad-hoc mixture of functions
    and function names.

    The language as a whole become more uniform from these additions and
    clarifications, making it easier to learn and use.  (See Esthetics.)

    Cost to Implementors:

    Interpreters which currently have a special case check for application
    of lambda expressions would need to modify this check to call
    FDEFINITION when a list of any sort is encountered.  Note that all
    Common Lisps already must perform some such check, since lambda
    expressions can be funcalled (and this is currently a very special case,
    the only standard case of a list being funcalled).  This means that
    every Lisp already has a place to insert the required call to
    FDEFINITION.

    In some implementations, FDEFINITION of a lambda expression could be that
    lambda-expression itself.  In others featuring a pre-eval codewalk, the
    walk would be done by FDEFINITION, which would return an appropriate
    closure.

    Cost of Non-adoption:

    Rather than two notions for function references (functions and function
    names), there would be several notions, each corresponding to the valid
    inputs for particular group of primitives.  APPLY and FUNCALL would
    accept functions, symbolic names, and lambda expressions, but not setf
    function names.  FDEFINITION and its kind would accept symbols and setf
    function names but not lambda expressions.  If the :LARGE proposal is
    not adopted, this fragmentation would also apply to the various syntaxes
    involving function names; some names would be acceptable to DEFUN
    but not to FLET, etc.

    - - - - - - - - - - - - -
    > 13. Declare that any function name is a suitable argument to FUNCALL and
    >     APPLY.  In such a case, the function name is passed to FDEFINITION,
    >     and the result (which may in turn be a function name) is called.

    I don't think this is such a good idea.  The case of automatically coercing
    a symbol to a function is needed because it provides a portable mechanism
    for indirect addressing of a function; I haven't seen a reason to need this
    for non-symbol function specs.  But more important is that coercing a
    symbol to a function is a trivial operation that is reasonable to do at
    run time on each call without adding a significant amount of overhead.
    FDEFINITION, on the other hand, is a much more expensive operation -- at
    best it might use GET to do a property list lookup, or it could be using
    string-append and INTERN to convert the name to a symbol.  In either case,
    I think this is more work than you want to do on each call.

    > 12. Declare that any lamba expression (i.e., a list whose car is LAMBDA and
    >     whose cdr is a well-formed lambda argument list and body) is a function
    >     name.  The effects of the function name accessors on lambda expressions
    >     are as follows.  FDEFINITION returns an implementation-defined value which
    >     is the function specified the lambda expression, closed in the global
    >     environment.  This FDEFINITION value cannot be changed by SETF.
    >     FBOUNDP always returns T, and MAKUNBOUND is an error.

    The exceptions for SETF and MAKUNBOUND show that this is not really as
    consistent as you might like.  Furthermore, the FUNCTION special form would
    have to treat a LAMBDA expression as a function, not a function name, in
    order for it to be lexically scoped.  It seems like this might just cause
    confusion rather than consistency.

Copyright 1996-2005, LispWorks Ltd. All rights reserved.