How to implement optional arguments in CHICKEN?

Viewed 431

I'm new to CHICKEN and Scheme. In my quest to understanding tail recursion, I wrote:

(define (recsum x) (recsum-tail x 0))
(define (recsum-tail x accum)
  (if (= x 0)
      accum
      (recsum-tail (- x 1) (+ x accum))))

This does what I expect it to. However, this seems a little repetitive; having an optional argument should make this neater. So I tried:

(define (recsum x . y)
  (let ((accum (car y)))
    (if (= x 0)
        accum
        (recsum (- x 1) (+ x accum)))))

However, in CHICKEN (and maybe in other scheme implementations), car cannot be used against ():

Error: (car) bad argument type: ()

Is there another way to implement optional function arguments, specifically in CHICKEN 5?

2 Answers

I think you're looking for a named let, not for optional procedure arguments. It's a simple way to define a helper procedure with (possibly) extra parameters that you can initialize as required:

(define (recsum x)
  (let recsum-tail ((x x) (accum 0))
    (if (= x 0)
        accum
        (recsum-tail (- x 1) (+ x accum)))))

Of course, we can also implement it with varargs - but I don't think this looks as elegant:

(define (recsum x . y)
  (let ((accum (if (null? y) 0 (car y))))
    (if (= x 0)
        accum
        (recsum (- x 1) (+ x accum)))))

Either way, it works as expected:

(recsum 10)
=> 55

Chicken has optional arguments. You can do it like this:

(define (sum n #!optional (acc 0))
  (if (= n 0)
      acc
      (sum (- n 1) (+ acc n))))

However I will vote against using this as it is non standard Scheme. Chicken say they support SRFI-89: Optional positional and named parameters, but it seems it's an earlier version and the egg needs to be redone. Anyway when it is re-applied this should work:

;;chicken-install srfi-89 # install the egg
(use srfi-89) ; imports the egg
(define (sum n (acc 0))
  (if (= n 0)
      acc
      (sum (- n 1) (+ acc n))))

Also your idea of using rest arguments work. However keep in mind that the procedure then will build a pair on the heap for each iteration:

(define (sum n . acc-lst)
  (define acc 
    (if (null? acc-lst) 
        0 
        (car acc-lst)))

  (if (= n 0)
      acc
      (sum (- n 1) (+ acc n))))

All of these leak internal information. Sometimes it's part of the public contract to have an optional parameter, but in this case it is to avoid writing a few more lines. Usually you don't want someone to pass a second argument and you should keep the internals private. The better way would be to use named let and keep the public contract as is.

(define (sum n)
  (let loop ((n n) (acc 0))
    (if (= n 0)
        acc
        (loop (- n 1) (+ acc n))))
Related