1
votes

Background: I was working on a project in guile 1.8.8 scheme a few weeks ago and, being a little rusty, I forgot about the built-in (reduce) function, so I rolled my own. A little later, I ran into what seemed to be a hopeless bug, where calling a function with no side effects changed the flow of the rest of the program (working properly and passing a few unit tests vs. crashing completely) AFTER the function had long since returned. Several pieces of code which used to return something like (A B C D) were now returning only (A), causing a multitude of problems.

Minimum working example: After several days of whittling, I cornered the problem into this small piece of stand-alone code:

(define (my-reduce fun ls)
    (if (null? (cdr ls))
        (car ls)
        (my-reduce fun (cons (fun (car ls) (cadr ls))
                             (cddr ls)))))

(format #t "~a "  (my-reduce +   '(1 2 3)))
(format #t "~a "  (my-reduce or  '(1 2 3)))
(format #t "~a~%" (my-reduce +   '(1 2 3)))

Which prints out 6 1 1, instead of the expected 6 1 6.

Additional observations:

  • Setting the second line to a + yields the expected 6 6 6.
  • Setting the second line to and yields 6 3 3.
  • Additional lines of + after these produce additional 1s or 3s depending on what the second line is set to. So, the sequence + or + + leads to the output 6 1 1 1.
  • Additional lines of and or or after the first do NOT switch the output back. So, if the sequence is + and or +, the output is 6 3 3 3. It seems as though, once I've passed or or and to (my-reduce), the function becomes permanently "stuck" having that as its argument.
  • I also notice that passing and or or to the built-in (reduce) function causes a type-error, since they are technically macros and not functions.
  • Along those same lines, I notice that swapping out or for (lambda (x y) (or x y)) yields the expected output. Therefore, it seems that the critical thing here is that passing macros to my home-rolled reduce function causes an issue.

Question: What is going on here? Why does calling (my-reduce) with and or or cause such unexpected behavior? Does it have to do with the fact that those "functions" are actually macros?

Thanks in advance for any help. This one has really stumped me!

1

1 Answers

3
votes

You said it yourself, you can't pass and, or as parameters where a function is expected because they're macros, and will complain with a "bad syntax" error. In fact, I can't understand how this even works for you:

(my-reduce or '(1 2 3))

The only way I can think of is that you redefined and, or as functions somewhere, and that is the source of the problems. As a side note, my-reduce (understood as a fold-right operation) can be implemented in a more standard way like this:

(define (my-reduce fun ls)
  (if (null? (cdr ls))
      (car ls)
      (fun (car ls)
           (my-reduce fun (cdr ls)))))

The above assumes that the list is non-empty, if that's not always the case, the usual is to pass as parameter an initial value:

(define (my-reduce fun init ls)
  (if (null? ls)
      init
      (fun (car ls)
           (my-reduce fun init (cdr ls)))))