Alex Shinn <xxxxxx@gmail.com> schrieb am Mi., 4. Jan. 2017 um 07:46 Uhr:
On Sat, Dec 31, 2016 at 3:13 AM, Marc Nieper-Wißkirchen <xxxxxx@gmail.com> wrote:
When these procedures are implemented with case-lambda (and when case-lambda is more than a mere macro keyword), it is not too hard for an implementation to dispatch on the number of arguments at compile/expansion time, rather than at evaluation time.

I really think case-lambda is the biggest wart in R7RS,
and needs to be discouraged at every opportunity.

While I agree with some (or most) of your points, you make about case-lambda, I won't call it a wart, and if I did, I think I would call other things in R7RS much bigger warts (cond-expand as an expression; or that the semantics of libraries is so much underspecified that any portable use of libraries is heavily restricted [an implementation may not need support recursive top-level definitions; an implementation may load a library more than once per program-run, thus making parameter objects across library boundaries or things like register-default-comparator! of SRFI 128 impossible]).
 
But complaining about case-lambda is missing the point. I mentioned case-lambda because it is the only construct for optional/rest argument handling in R7RS. My point holds when an implementation uses let-optionals, DSSSL, match-lambda instead of case-lambda and when these alternative constructs are supported by the static-code analysis of that particular implementation.

It's possible to provide a more general approach to
optimizing variadic arguments without forcing a specific,
limited syntax.  For example, using Chibi's plugin
optimization infrastructure:

$ chibi -mchibi.disasm -e'(disasm (lambda a (let ((x (if (pair? a) (car a) 0))) (+ x 1))))'
      -------------- 0x107627320
      LOCAL-REF 0
      TYPEP 6
      JUMP-UNLESS 27 L1
      LOCAL-REF 0
      CAR 
      JUMP 17 L2
L1:   PUSH 0
L2:   PUSH #<procedure #f>
          -------------- 0x1076272c0
          PUSH 1
          LOCAL-REF 0
          ADD 
          RET 
      TAIL-CALL 1
      RET 
$ chibi -mchibi.{disasm,optimize.rest} -e'(disasm (lambda a (let ((x (if (pair? a) (car a) 0))) (+ x 1))))'
      -------------- 0x1016e6f60
      PUSH #<undef>
      FCALL0 0x10172c860 "num-parameters"
      PUSH 0
      LT 
      JUMP-UNLESS 26 L1
      LOCAL-REF 1
      JUMP 17 L2
L1:   PUSH 0
L2:   LOCAL-SET -5
      PUSH 1
      LOCAL-REF -5
      ADD 
      RET 

While for most use cases (optional parameters), the syntax and semantics is definitely not optimal (you mentioned the quadratic code size, for example), I see one use case, for which it seems to fit:

(define f
  (case-lambda
    ((x y z) (* x y z))
    (args (error "f has to be invoked with exactly three arguments"))))

How would you code this without case-lambda so that optimal code is produced when f is well-known at a call-site?
 
With the optimization enabled, no rest list will be consed.
[Beware, this is just a proof-of-concept and not well tested,
but the principle is sound.]  These sorts of optimizations
make heavy use of pattern matching and AST fiddling,
which is more difficult if you have lots of different AST types
like `case-lambda', or `let' as I sometimes see,

NB: In my Rapid Scheme front-end, I am currently using case-lambda as a core form, but it does replace lambda, so the number of core forms do not change. And handling case-lambda-nodes is no more difficult than handling lambda-nodes in an AST (you just have one more loop, over the clauses).

--

Marc