Is it possible to "extend" a function / lambda / macro in Scheme?

inheritance, r5rs, r6rs, r7rs, scheme

Solution

Rather than using `import`, a better solution is to keep track of the original function by `let`-binding it. It's also better to check that the type of the argument is a string, rather than that it is not a number. Using both of these approaches means that it's possible to compose the technique.

(define +
  (let ((old+ +))
    (lambda args
      (if (string? (car args))
          (apply string-append args)
          (apply old+ args)))))

(define +
  (let ((old+ +))
    (lambda args
      (if (vector? (car args))
          (apply vector-append args)
          (apply old+ args)))))

The above will produce a `+` function that works on numbers, strings, or vectors. In general, this is a more extensible approach.

I was able to verify that the above works correctly in MIT/GNU Scheme, Guile, Racket, Chicken, TinyScheme, and SCSH. However, in some implementations, such as Biwa Scheme, it is necessary to use `set!` instead of `define`. In Ikarus, `set!` cannot be used on an imported primitive, and `define` messes up the environment, so it is necessary to do this in two steps:

(define new+
  (let ((old+ +))
    (lambda args
      (if (string? (car args))
          (apply string-append args)
          (apply old+ args)))))
(define + new+)

Note that according to R5RS, `define` and `set!` are supposed to be equivalent in this case:

At the top level of a program, a definition

(define <variable> <expression>)

has essentially the same effect as the assignment expression

(set! <variable> <expression>)

if `<variable>` is bound.

Problem

For example: if I want the function `equal?` recognize my own type or record, can I add a new behavior of `equal?`? without erasing or overwriting the old one? Or for example if I want to make the function `"+"` accept also string?

Original source