FUNCTION-COERCE-TIMEFUNCTION. This functionality can be very useful, but the time at which the coercion is accomplished must be made clear.LAMBDA (&REST ARGUMENTS) (APPLY F ARGUMENTS))COERCE F 'FUNCTION) had been passed instead.Also, inlining of mapped functions (without using temporary storage to hold a cached value of the function being mapped) can be done without needing to know whether the function being inlined will affect the place which holds the functional argument being passed.
SORT or MAPCAR). Otherwise, if the functional argument's use persists beyond the end of the call to the operator (eg, SET-MACRO-CHARACTER), any coercion is delayed until the function is about to be called and is done anew every time the function is to be called.
That is, functions like SORT and MAPCAR are permitted to treat a non-function functional argument, F, as if (COERCE F 'FUNCTION) had been passed instead. However, functions like SET-MACRO-CHARACTER would treat a non-function functional argument, F, as if #'(LAMBDA (&REST ARGUMENTS) (APPLY F ARGUMENTS)) were passed instead.
SET-MACRO-CHARACTER without needlessly hampering useful optimizations to things like SORT or MAPCAR, which very rarely require this facility.DEFVAR *SOME-FUNCTIONS* (LIST #'(LAMBDA (X) (SETF (SYMBOL-FUNCTION 'FOO) X) 1) #'(LAMBDA (X) (SETF (SYMBOL-FUNCTION 'FOO) X) 2) #'(LAMBDA (X) (SETF (SYMBOL-FUNCTION 'FOO) X) 3) #'(LAMBDA (X) (SETF (SYMBOL-FUNCTION 'FOO) X) 4)))
; Control situation A (PROGN (SETF (SYMBOL-FUNCTION 'FOO) (CAR *SOME-FUNCTIONS*)) (LIST (MAPCAR #'(LAMBDA (&REST X) (APPLY #'FOO X)) *SOME-FUNCTIONS*) (FOO T))) => ((1 1 2 3) 4)
; Control situation B
(LET ((FOO (SETF (SYMBOL-FUNCTION 'FOO) (CAR *SOME-FUNCTIONS*))))
(LIST (MAPCAR FOO
*SOME-FUNCTIONS*)
(FOO T)))
=> ((1 1 1 1) 4)
; Interesting Situation 1
(PROGN (SETF (SYMBOL-FUNCTION 'FOO) (CAR *SOME-FUNCTIONS*))
(LIST (MAPCAR #'FOO
*SOME-FUNCTIONS*)
(FOO T)))
=> ((1 1 2 3) 4) ;Lazy-1
or ((1 1 1 1) 4) ;Ambitious-1
; Interesting Situation 2
(PROGN (SETF (SYMBOL-FUNCTION 'FOO) (CAR *SOME-FUNCTIONS*))
(LIST (MAPCAR 'FOO
*SOME-FUNCTIONS*)
(FOO T)))
=> ((1 1 2 3) 4) ;Lazy-2
or ((1 1 1 1) 4) ;Ambitious-2
(DEFUN SHARP-DOLLAR (STREAM CHAR N) (DECLARE (IGNORE CHAR)) (EXPT (READ STREAM) (OR N 1)))
(SET-DISPATCH-MACRO-CHARACTER #\# #\$ 'SHARP-DOLLAR)
(VALUES (READ-FROM-STRING "(#$3 #4$3)")) => (3 81)
(DEFUN SHARP-DOLLAR (STREAM CHAR N) (DECLARE (IGNORE CHAR)) (+ (READ STREAM) (* (OR N 0) 0.01)))
(VALUES (READ-FROM-STRING "(#$3 #4$3)"))
=> (3.0 3.04) ;Lazy-3
(3 81) ;Ambitious-3
Proposal LAZY requires LAZY-1, LAZY-2, LAZY-3. Proposal AMBITIOUS requires AMBITIOUS-1, AMBITIOUS-2, AMBITIOUS-3. Proposal HYBRID requires AMBITIOUS-1, AMBITIOUS-2, LAZY-3.
(In the test case, LAZY-1 can occur only if MAPCAR is inlined, and then only if the original value of the function definition is not cached.)
[Some info below is based on empirical testing -- I didn't look at the source or try to see how speed/safety affect things. -kmp]
Symbolics Genera implements AMBITIOUS-1 interpreted and LAZY-1 compiled. Symbolics Cloe (a compiled-only lisp) implements LAZY-1 both `interpreted' and compiled.
Both Symbolics Genera and Symbolics Cloe implement LAZY-2.
Symbolics Genera implements LAZY-3. Symbolics Cloe implements AMBITIOUS-3.
HYBRID has the benefit of making things like readmacros easier to debug.
LAZY offers the additional benefit of being able to repair certain functional arguments to SORT or MAPCAR (eg, as a symbol) in the debugger, and then to proceed the error (in implementations offering that restart option) in a way that makes the ongoing call to SORT or MAPCAR notice the repairwork right away.
FUNCTION-COERCE-TIME:HYBRID.
Additional comments:
There was some concern about the proposal wording, because it was trying to allow for the possibility that implementations might extend other objects (e.g., lists that begin with LAMBDA) to "coerce" to functions, and the proposal should apply for them, too.
This made the wording of the proposal pretty awkward, though. - - - - - - The writeup for the LAZY option should mention that this might cause a substantial performance hit in some implementations.
Of the options presented, I prefer AMBITIOUS. However, I would really much rather see this whole issue left explicitly vague in the standard. - - - - notes from Cleanup meeting (Fairfax, 1988):
Eliminate the AMBITIOUS proposal. Continue to evolve the HYBRID and LAZY variants.
Relation to DEBUG quality.
There was some discussion about the idea of permitting vagueness (ie, making LAZY/AMBITIOUS optional in some places).
X3J13 meeting:
Some people weren't completely convinced that the HYBRID proposal would feel predictable enough. Others disagreed. - - - - - - - - Well, the main reason why I prefer "AMBITIOUS" to "HYBRID" is that it seems kind of peculiar to make an exception for the two functions, SET-MACRO-CHARACTER and SET-DISPATCH-MACRO-CHARACTER. Besides being different from all the other functions that take functional arguments, it makes them different from the pathname functions (which always coerce non-pathname "pathname" arguments to pathnames) and the package functions (which always coerce non-package "package" arguments to packages).
Also, I disagree that there is no performance penalty, although it's certainly small in comparison to the rest of the reader's processing. For example, A-Lisp has a fast, opencoded funcall primitive that it uses when its argument is guaranteed to be a function, which is *much* faster than a normal funcall (by a factor of at least 20).
I don't feel really strongly about this -- HYBRID is not really all that objectionable to me, and I would vote for it if AMBITIOUS is thrown out. ------- ... I'm thinking of a revision which might lean more toward explicitly vague on some of these issues. At the same time, I'd like to encourage the use of the DEBUG and/or SPEED quality to help compilers lean toward LAZY in the slow/easy-to-debug case and AMBITIOUS in the fast/hard-to-debug case. There are some details to be worked out, though...