-1

I am following the SICP lectures from MIT, and this is what I tried to find the square root approximation of a number by Heron of Alexandria's method. This is my first time trying out lisp, sorry for making noobie mistakes.

(define guess 1)

(define (avg a b)
  (/ (+ a b) 2))

(define (try guess x)
  (if (goodEnough guess x)
      guess
      (improve guess x)))

(define (improve guess x)
  (define guess (avg guess (/ x guess)))
  (try guess x)
  )

(define (goodEnough guess x)
  (= guess (avg guess (/ x guess))))

(print (try 1 25))

I am using Chicken scheme compiler to print this. This is the output:

Error: (/) bad argument type: #<unspecified>

    Call history:

    1.a.SquareRootApproximation.scm:29: try   
    1.a.SquareRootApproximation.scm:17: goodEnough    
    1.a.SquareRootApproximation.scm:27: avg   
    1.a.SquareRootApproximation.scm:19: improve     <--

Updated: I have changed my approach towards this problem using lisp with more abstraction, yet I can't figure out what this new error wants to imply. Any fixes? Thanks!

twodee
  • 606
  • 5
  • 24

1 Answers1

2

The value #<unspecified> is basically "void" in other languages. It is used as a return value whenever some procedure has nothing useful to return (for example, print will return this). It is also in some situations used as a temporary placeholder value, for example when handling an inner define.

Normally this temporary placeholder should not be visible to the user of the language, but it appears you've hit a strange edge case in the language (congratulations! This happens rarely). The error happens because (define guess (avg guess (/ x guess))) in the improve procedure is simultaneously defining a variable and using that variable. The behaviour of doing this is not well-specified, and some Scheme implementations will do what CHICKEN is doing (Guile, Gauche, Gambit) whereas others will give a somewhat more meaningful error message (MIT, Scheme48, Racket). The reason this is ill-specified has to do with the fact that inner define expands to letrec, because it allows mutually recursive procedures to be defined, but that creates a bit of an issue: what should happen for (define a b) (define b a), for example?

Your intention seems to be using the old guess variable that's passed as input to the procedure, so instead of using define you could use let to bind a new value for guess (how this should behave is well-specified), or just use a different name for it, like new-guess.

sjamaan
  • 2,282
  • 10
  • 19
  • I haven't learnt about let yet, I am still on the first lecture. Maybe new guess is the correct way to go for it. Thanks. It actually worked with the new variable. – twodee Sep 11 '17 at 10:37
  • It is very well specified since in R5RS and earlier `define` in `lambda` (and derived forms) are `letrec` not allowing any of the variables be evaluated before the body and in R6RS and later it is a `letrec*` which allows for already initialized variables to be evaluated. Of course none of these allow for evaluating a variable that is shadowed by **any** local `define` in the same `lambda`. `letrec` and `letrec*` is to allow to make recursive functions and thus the binding need to exist at closure creation time. – Sylwester Sep 11 '17 at 18:46
  • It is well-specified in the sense that "it is an error" to refer directly in the `` forms to the value of any ``. However, this basically means "the implementation is free to do whatever it wants". In essence, it's similar to "undefined behaviour" in C. Anything that "is an error" falls outside the spec and programs that cause such an error may or may not run/compile. This is very implementation-dependent, as you can see from my example: MIT, Racket and Scheme48 behave very differently from CHICKEN, Guile, Gambit and Gauche. – sjamaan Sep 12 '17 at 07:14