4

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?

cjp
  • 45
  • 5

2 Answers2

3

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
Óscar López
  • 232,561
  • 37
  • 312
  • 386
  • So, in the named let, ((x x) (accum 0)) means "x will be defaulted to x, accum will be defaulted to 0?" – cjp Jun 11 '19 at 21:28
  • 1
    It's variable bindings, like a normal `let`. Assign `x` to `x` (or any other name you want) and `0` to `accum`. Nice thing is, these assignments occur only once, at the beginning (unlike my second solution, which evaluates the `let` at every iteration). – Óscar López Jun 11 '19 at 21:32
  • Ok, and obviously these bindings are only set initially, not on the recursive call at the end of the function. – cjp Jun 11 '19 at 21:35
3

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))))
Sylwester
  • 47,942
  • 4
  • 47
  • 79
  • Interesting that rest arguments build a pair on the heap for each iteration. This suggests rest arguments would never be appropriate for a recursive function. Do you agree? – cjp Jun 11 '19 at 21:31
  • 1
    @cjp It's remnants from the 60s and the interpreted lisp era. Today Scheme and Common Lisp are stack machines and arguments are pointers on a stack so then when you have rest arguments and just apply it might optimize it, but in the event you start doing `car` on it it has to make a list of the extra arguments on the stack which ends up being on the heap. Rest arguments are great for the flexibility of a contract, but for recursion it's not a good match. Instead of doing rest and perhaps apply it's better to just pass the list as one argument in the recursive procedure. – Sylwester Jun 11 '19 at 21:50
  • The `#!optional` and `#!key` constructs are from DSSSL and they are implemented by collecting rest args into a list and picking that apart. Theoretically, they could be implemented in a more efficient way. It shouldn't even be too hard to implement that, but it simply hasn't been implemented at this moment. Most vararg intrinsics (like `+` and `=`) actually do not first build a list. – sjamaan Jun 12 '19 at 06:39
  • Proper rest args can also be optimised with the [`case-lambda`](http://wiki.call-cc.org/man/5/Module%20(chicken%20base)#case-lambda) construct from R7RS / SRFI-16. The same caveat applies here by the way: it could be done, but currently isn't. Come to think of it, I think this might be a nice little project for me to tackle for CHICKEN 5.2... – sjamaan Jun 12 '19 at 06:40
  • Yeah, this seems like a fun little exercise. I made a [ticket](https://bugs.call-cc.org/ticket/1623) for it. – sjamaan Jun 12 '19 at 06:45