inheritanceschemer5rsr6rsr7rs

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


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?


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.