On Thu, 6 Jan 2005, Christopher Dutchyn wrote: > On Thu, 6 Jan 2005, Taylor Campbell wrote: > > > However, it might be a bit deceptive or confusing in the syntax, in that > > it looks like a regular procedure call at first, though it's not. > > Yes; but it's just as surprising to encounter a guard? and then => too. One could find anything that one is unfamiliar with surprising. I'm not sure what you find surprising in specific about that, however, and the deception that it could look like a procedure call is very specific & concrete. Furthermore, the order I chose is more consistent with the order in which things are evaluated: first the generator, then the guard, and finally -- conditionally -- the receiver. > > Also, the guard is usually in that position in similar constructions > > that linearize nested conditionals: SYNTAX-CASE, Andrew Wright's MATCH, > > &c. > > I don't know MATCH, but SYNTAX-CASE must place fender-expr second because > it doesn't have the special "=>" marker. No, there is no such constraint. It could just as easily put the guard first. > Do you think of the guard as > optional? If so, then you're logically talking about sub-casing the > existing => clause, not adding another cond clause syntax. Recast your > proposal in those terms. If it helps you to think of it that way (with the default guard being the identity function), go ahead; the way I wrote the specification was intended to be as simple & straightforward as possible. I'm not going to change the wording just because you demand that it better match your thoughts. > > Yes. What you suggest here are some workarounds for the real problem, > > which is having more general way to conditionalize. Essentially, this > > proposal more cleanly separates the condition from a temporary value > > that is useful if the condition holds true, while the existing COND => > > syntax multiplexes the temporary value and the condition. > > On what basis do you believe that the value is temporary? See my comments > below. I was using the word loosely, for the purposes of describing what the new COND clause does. 'Local' or 'generated' value might have been better choices of words, but it is not as significant as you make it out to be. > > Not only do your suggested workarounds require either writing several > > new routines for every possible application or inserting clumsy IFs in > > COND clauses, > > I would construct something like > (define (anaphoric x) (if x x #f)) ;;make macro and with-values easily > and get > (cond > (((anaphoric char?) (read-char port)) => (lambda (c) ...)) > ...) That is still a workaround that requires inserting a few extra tokens into the conditional and still exhibits all of the temporary boxing problems. > > but they may also require complicating the matter by constructing > > temporary boxes to hide #F if it is a possible useful temporary value. > > I don't see how your proposal avoids this ... #F means "go to next clause" > for cond. Maybe I don't understand this point; can you explain? If #F is considered a useful value -- e.g., as a table entry --, then it would not work for the conditional part of a (<conditional> => <receiver>) clause to return #F, because that would make the clause fail. Instead, it must box #F somehow to pass to the receiver, so that COND doesn't take its result to mean failure, and the receiver must remove the contents of the box. > > This overhead is also necessary in order to support multiple possibly > > useful temporary values, which you must store in a temporary list; such > > a list is harder for a compiler to optimize than the way I have designed > > it. > > Convince me that the guard will *always* treat the value(s) as temporary: > ie. the guard doesn't memoize or perform some other operation that forces > it to be a listified anyway. Otherwise, you can't safely do what you're > proposing, unless the guard is so simple that a decent peephole or > inter-procedural optimizer can see its way clear to doing this. I'm not trying to convince you of that. It isn't true. What I _did_ argue is that the way it is currently designed is easier to optimize for most cases than your kludgey workarounds. Even ignoring boxing of #F, what you suggest for multiple return values requires boxed lists. For example, consider your MY-PROJ0 procedure (which probably ought to have a different name, since it is very different from PROJ0): it takes any number of arguments; if the first argument is true, it returns a list containing all of the arguments. This is harder to analyze & optimize: even if it is a known procedure, only if either all call sites are also known or the call is integrated can the list be easily unboxed by the compiler. With my design, the list is never returned: it is only propagated to different procedures in quick succession. What those procedures do is immaterial: if the guard stores its argument list somewhere, sure, a list will have to be allocated. However, the design I constructed does not _intrinsically_ necessitate allocating a list, unless the compiler does not even optimize rest argument propagation. It is specific to the guard anyway. In brief, your workarounds are harder to optimize in the general case, whereas mine are trivial, unless the specific application intrinsically uses a heap-allocated list anyway, in which case the point is moot.