[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Issue: FUNCTION-COERCE-TIME (Version 2)
Date: 15 Sep 88 18:32 PDT
From: masinter.pa@Xerox.COM
Subject: Issue: FUNCTION-COERCE-TIME (Version 1)
I don't have any further correspondence on file after this.
Did you send a revision?
Nope, but here's one now. I changed the wording significantly enough
that each warrants a careful re-reading. Anyway, hope this satisfies
your criticisms about vagueness.
-----
Issue: FUNCTION-COERCE-TIME
References: SET-MACRO-CHARACTER (p362),
SET-DISPATCH-MACRO-CHARACTER (p364),
MAP (p249), MAPL (p129), ...
Functions using :TEST, :KEY, etc. (REDUCE, MEMBER, DELETE, ...)
Functions using a positional predicate (SORT, DELETE-IF, ...)
Category: CLARIFICATION
Edit history: 20-Jun-88, Version 1 by Pitman
16-Sep-88, Version 2 by Pitman
(changes to presentation of all proposals per Masinter's comments)
Status: For Internal Discussion
Problem Description:
Many functions which accept arguments which are to be treated functionally
but which are not of type FUNCTION. This functionality can be very useful,
but the time at which the coercion is accomplished must be made clear.
Proposal (FUNCTION-COERCE-TIME:LAZY):
Specify that when a non-function (eg, a symbol) is used as a functional
argument to an operator, the coercion of that non-function to a function
is delayed until the function is about to be called and is done anew
every time the function is to be called. That is, passing a non-function
functional argument, F, is equivalent to passing
#'(LAMBDA (&REST ARGUMENTS)
(APPLY F ARGUMENTS))
Rationale:
One of the main reasons that it may be useful to pass a non-function
instead of a function is to accomplish indirection which allows later
redefinitions to take proper effect. Early binding would tend to
thwart the usefulness of such indirection and force people into
notationally more clumsy devices.
Proposal (FUNCTION-COERCE-TIME:AMBITIOUS):
Specify that when a non-function (eg, a symbol) is used as a functional
argument to an operator, the coercion of that non-function to a function
is done immediately. That is, all such functions treat a non-function
functional argument, F, as if
(COERCE F 'FUNCTION)
had been passed instead.
Rationale:
This is easier to implement since the coercion is done up front and
no further worry about uncoerced functions is needed internally.
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.
Proposal (FUNCTION-COERCE-TIME:HYBRID):
Specify that when a non-function (eg, a symbol) is used as a
functional argument to an operator, the coercion of that non-function
to a function must be done immediately if the functional argument is
to be used only internally to the function (eg, 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.
Rationale:
Debugging is enhanced for operations such as SET-MACRO-CHARACTER
without needlessly hampering useful optimizations to things like
SORT or MAPCAR, which very rarely require this facility.
Test Cases:
(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.
Current Practice:
Implementations are permitted to differ in when they do the coercion since
the coercion time is not clearly spelled out.
(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.
Cost to Implementors:
[Costs may vary widely depending on current practice.
I'll leave this one open for now pending feedback from others. -kmp]
Cost to Users:
This change is upward compatible.
Cost of Non-Adoption:
A very important aspect of the language would be left unspecified.
Portability would suffer.
Benefits:
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.
Aesthetics:
Since this is a visible aspect of the language, anything which clarified
the behavior that a programmer might expect would seem to improve the
aesthetics somewhat.
Discussion:
This issue was raised by Nick Gall and written up by Pitman.
Pitman supports FUNCTION-COERCE-TIME:HYBRID.