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
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
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.