This is the mail archive of the guile@cygnus.com mailing list for the guile project.
Index Nav: | [Date Index] [Subject Index] [Author Index] [Thread Index] | |
---|---|---|
Message Nav: | [Date Prev] [Date Next] | [Thread Prev] [Thread Next] |
Per Bothner counters: > > Maybe I've been using perl too long :-) but I prefer to introduce an > > "lvalue" object type and provide automatic lvalue->rvalue conversion > > as the context demands. > > The problem is that you have to do this conversion *everywhere*, > except in lvalue contexts. And except where the compiler knows an rvalue is expected, in which case an rvalue would be passed. > Having first-class lvalue objects is > fine - unless you want to have automatic dereferences to rvalues. > The only language I know that did that was Algol68, and they had > the luxury of static typing (and thus a better concept of > "expected type"). Upon reflection, I can see that my proposal would require a simple form of static typing, but only in order for code to be optimized properly. Functions would be allowed to have prototypes indicating whether each arg is an lval, an rval, or any val, and likewise for the returned object. If the compiler knows all this information where the procedure is defined and called, I don't think there would need to be any additional run-time overhead. > Propose something concrete and implementable. Since you ask... (define (add-n-double x y) (* 2 (+ x y))) would be optimally typed as (define (rvalue (add-n-double (rvalue x) (rvalue y))) (* 2 (+ x y))) since it must receive and return rvalues. In order to safely avoid the runtime check for rvalue-ness, code that calls this function needs to see a prototype, such as (prototype rvalue (add-n-double (rvalue x) (rvalue y))) On the other hand, a function that operates on its argument should be declared to take an lvalue: (define (lvalue (incr (lvalue x))) (set! x (1+ x)) x) For compatibility with existing code, in the absence of type specifiers, all arguments are checked and converted to rvalues if necessary. Type specifiers are necessary for best optimization. You are allowed to redefine a public function only if the new version has a compatible signature. A function call's semantics are as follows: (f (g)) If f has been declared to take an lvalue arg, and g has been declared to return an rvalue, this is an error. If f has been declared to take any value, g's return value is passed to f as-is. A prototype for such an f looks like one of these: (prototype (f x)) ;; or (prototype lvalue (f x)) ;; or (prototype rvalue (f x)) If the declared type of f's first arg matches g's return type, the value is passed without change. If g's return type is "any" (or, equivalently, if g lacks a prototype), and f takes an rvalue, a run-time check and possible conversion is performed. If g's type is any and f takes an lvalue, a run-time check and possible error occurs (since an rvalue returned by g cannot be converted). Finally, if g must return an lvalue and f takes an rvalue, a run-time conversion occurs with no type-checking. I am avoiding discussion of how to handle variable-length argument lists, because I haven't though it through yet. > Note I am also adding to Kawa first-class "location" ("reference") > values, but they have to be explicitly dereferenced. That sounds like something Guile could benefit from, independently of automatic lvalues. > > But, maybe that's because I don't understand. > > That seems plasusible. At least I suspect you don't understand your > own proposal. I fear, rather, that my proposal is so radical that an implementation of it could no longer be called Scheme. -John