Exception: multiple definitions for function in body

206 views Asked by At

I have a simple program:

(import (rnrs))
(define (abs x)
    (cond ((> x 0) x)
           ((= x 0) 0)
           ((< x 0) (- x))
           ))
(define (square x)
    (* x x))
(define (sum-sq x y)
    (+ (square x) (square y)))
(display
    (sum-sq (read) 3))

When I run it, I have an exception; what did I do wrong?

/home# scheme-script /home/scheme/main.ss
Exception: multiple definitions for abs in body (top-level-program #<annotation /home/scheme/main.ss[0:15] (import (...))> #<annotation /home/scheme/main.ss)[17:122] (define (...) (...))> #<annotation /home/scheme/main.ss[124:156] (define (...) (...))> #<annotation /home/scheme/main.ss[158:210] (define (...) (...))> #<annotation /home/scheme/main.ss[212:244] (display (...))> near line 1, char 1 of /home/scheme/main.ss

2

There are 2 answers

0
ad absurdum On BEST ANSWER

It is illegal to redefine a variable in a top-level program in R6RS Scheme. OP has executed the script using scheme-script; in Chez Scheme scheme-script is equivalent to scheme --program, which treats the file as a top-level program. It is fine to redefine things in the REPL when you are experimenting with definitions, unless you wrap that code in a top-level-program form.

scheme --script treats the file as a shell script, while scheme-script (i.e., scheme --program) treats it as a top-level program. To demonstrate that OP's problem is a result of the file being treated as a top-level program, run it using scheme --script, then wrap the posted code in a (top-level-program ...) form and try running again with scheme --script. The first attempt will execute successfully, and the second will again raise an exception.

One solution to OP's problem is to use scheme --script instead of scheme-script (or scheme --program). Of course, one could simply use the built-in abs procedure, or rename the new procedure as, e.g., my-abs.

But, sometimes you really do want to use an identifier that has previously been claimed by some library that you need to import. For that situation there is except. Here is a version of OP's code using except in the import form:

(import (except (rnrs) abs))

(define (abs x)
  (if (< x 0) (- x) x))

(define (my-abs x)
  (<))
(define (square x) (* x x))

(define (sum-sq x y)
  (+ (square x) (square y)))

(display "Enter a number: ")
(let ((x (read)))
  (display x) (display "^2 + 3^2 = ") (display (sum-sq x 3)) (newline))

(display "Enter a number: ")
(let ((x (read)))
  (display "|") (display x) (display "| = ") (display (abs x)) (newline))

;;; Easier with Chez Scheme `format`, which can be made available by
;;; changing the `import` form at the top to:
;;;
;;;  (import (except (chezscheme) abs))

;; (display "Enter a number: ")
;; (let ((x (read)))
;;   (format #t "~A^2 + 3^2 = ~A~%" x (sum-sq x 3)))

;; (display "Enter a number: ")
;; (let ((x (read)))
;;   (format #t "|~A| = ~A~%" x (abs x)))

This program imports all identifiers which are exported from the rnrs library, except for abs. The top-level program is then free to define an abs identifier.

$ scheme-script top-level.ss 
Enter a number: 4
4^2 + 3^2 = 25
Enter a number: -42
|-42| = 42

What the Standard Says

A top-level program is like a library, except that it cannot contain export forms. From R6RS 7.1. Library form:

... no identifier can be imported multiple times, defined multiple times, or both defined and imported.

The specification continues:

The expressions of variable definitions are evaluated from left to right, as if in an implicit letrec*....

But, by 11.4.6. Binding constructs, in a letrec* form:

(letrec* <bindings> <body>)‌‌ syntax

Syntax: <Bindings> must have the form

((<variable1> <init1>) ...),

where each <init> is an expression, and <body> is as described in section 11.3. Any variable must not appear more than once in the <variable>s.

So an identifier can't be imported multiple times, both imported and defined, or defined and redefined in a library or top-level program. The OP code violates this by both importing a definition for an identifier, and redefining that identifier within the top-level program.

0
Óscar López On

The error message is quite explicit: you're redefining abs, which is a built-in procedure. Depending on the Scheme interpreter in use, this can be a problem - in particular, you can't redefine a procedure in Chez Scheme. Simply delete abs, it's already provided by the language.