"Life is all about sharing. If we are good at something, pass it on." - Mary Berry

SICP Exercise 2.77: expected a procedure that can be applied to arguments, given #f

2023-07-13

Categories: Programming

2.5.1 Generic Arithmetic Operations

Louis Reasoner tries to evaluate the expression (magnitude z) where z is the object shown in figure 2.24. To his surprise, instead of the answer 5 he gets an error message from apply-generic, saying there is no method for the operation magnitude on the types (complex).

To simplify the install-complex-package procedure, I made the following changes:

 1(define (install-complex-package)
 2  (define (make-from-real-imag x y)
 3		((get 'make-from-real-imag 'rectangular) x y))
 4	(define (tag z) (attach-tag 'complex z))
 5	(put 'make-from-real-imag 'complex
 6		(lambda (x y) (tag (make-from-real-imag x y))))
 7
 8(install-complex-package)
 9
10(trace-define (make-complex-from-real-imag x y)
11	((get 'make-from-real-imag 'complex) x y))
12
13(magnitude (make-complex-from-real-imag 3 4))

However, upon running the code, instead of the expected error from apply-generic, a different error occurred:

1$ racket 2.77-apply-generic-magnitude.rkt
2'done
3>(make-complex-from-real-imag 3 4)
4application: not a procedure;
5 expected a procedure that can be applied to arguments
6  given: #f

I wondered why it returned #f when 'make-from-real-imag 'complex had already been put into the dispatch table:

1$ racket --repl
2Welcome to Racket v8.9 [cs].
3> (require "2.77-apply-generic-magnitude.rkt")
4> (get 'make-from-real-imag 'complex)
5#<procedure:...neric-magnitude.rkt:75:16>

To debug the issue, I utilized the racket/trace library:

 1$ racket --repl
 2Welcome to Racket v8.9 [cs].
 3> (require "2.77-apply-generic-magnitude.rkt")
 4>(install-complex-package)
 5> (put 'make-from-real-imag 'complex #<procedure:...neric-magnitude.rkt:75:16>)
 6< #<void>
 7<'done
 8'done
 9> (make-complex-from-real-imag 3 4)
10>(make-complex-from-real-imag 3 4)
11> (get 'make-from-real-imag 'complex)
12< #<procedure:...neric-magnitude.rkt:75:16>
13> (make-from-real-imag 3 4)
14> >(get 'make-from-real-imag 'rectangular)
15< <#f
16application: not a procedure;
17 expected a procedure that can be applied to arguments
18  given: #f
19 [,bt for context]

As a result, it became evident that when we called (make-complex-from-real-imag 3 4), it was actually invoking make-from-real-imag 3 4, which, in turn, called (get 'make-from-real-imag 'rectangular) and this returned #f.

To address this issue, we need to add the install-rectangular-package procedure mentioned in 2.4.3 Data-Directed Programming and Additivity to get the expected error:

 1>(make-complex-from-real-imag 3 4)
 2> (get 'make-from-real-imag 'complex)
 3< #<procedure:...neric-magnitude.rkt:75:16>
 4> (make-from-real-imag 3 4)
 5> >(get 'make-from-real-imag 'rectangular)
 6< <#<procedure:...neric-magnitude.rkt:65:16>
 7> (attach-tag 'rectangular '(3 . 4))
 8< '(rectangular 3 . 4)
 9>(tag '(rectangular 3 . 4))
10>(attach-tag 'complex '(rectangular 3 . 4))
11<'(complex rectangular 3 . 4)
12>(get 'magnitude '(complex))
13<#f
14No method for these types: APPLY-GENERIC '(magnitude (complex))

Tags: sicp racket scheme

Edit on GitHub

Related Posts: