sicp-ex-2.78



<< Previous exercise (2.77) | Index | Next exercise (2.79) >>


 ;; ----------------------------------------------- 
 ;; EXERCISE 2.78 
 ;; ----------------------------------------------- 
  
 (define (attach-tag type-tag contents) 
   (if (number? contents) 
       contents 
       (cons type-tag contents))) 
  
 (define (type-tag datum) 
   (cond ((number? datum) 'scheme-number) 
         ((pair? datum) (car datum)) 
         (else (error "Wrong datum -- TYPE-TAG" datum)))) 
  
 (define (contents datum) 
   (cond ((number? datum) datum) 
         ((pair? datum) (cdr datum)) 
         (else (error "Wrong datum -- CONTENTS" datum)))) 

Siki

The answer above doesn't follow the requirement in this exercise. The right answer should be:

  
 (define (type-tag datum) 
   (cond ((number? datum) datum) 
         ((pair? datum) (car datum)) 
         (else (error "Wrong datum -- TYPE-TAG" datum)))) 
  
 (define (contents datum) 
   (cond ((number? datum) datum) 
         ((pair? datum) (cadr datum)) 
         (else (error "Wrong datum -- CONTENGS" datum)))) 
  
 (define (attach-tag tag content) 
   (cond ((number? content) content) 
         ((pair? content) (cons tag content)))) 
  


The above correction is wrong. The original answer is correct.

The differences in and problems with the correction are as follows:

torinmr

Hmmm, I believe that the top version does have a bug, but it's not the one mentioned by Siki. The bug is that the condition in attach-tag should be

 (eq? type-tag 'scheme-number) 

Instead of

 (number? contents) 

This is because it's possible to create a tagged data type where the contents is just a number, but the type-tag is not scheme-number. (For example, suppose we implemented a "logarithm" type for storing very large numbers, where the stored value was the natural logarithm of the number we were representing.)


noname

If the above is correct, then the book itself is wrong, as it says explicitly to represent native numbers without the 'scheme-number symbol.


atomik

I agree with torinmr. Checking the type-tag argument passed to attach-tag eliminates any ambiguity about what type the user wants the contents of their datum to have. Native numbers can still be represented if the attach-tag function simply returns the contents parameter like so:

 (define (attach-tag type-tag contents) 
     (if (eq? type-tag 'scheme-number) 
         contents 
         (cons type-tag contents))) 

This does not violate the instructions given in the book which say "ordinary numbers should be represented simply as Scheme numbers rather than as pairs whose car is the symbol 'scheme-number"

Too elaborate on the example torinmr gave, if the user wants to run

 (attach-tag 'log 5) 

the version given in this comment will return '(log 5). The implementations given at the top of the page will return 5, thus breaking any operations which expect data tagged with the 'log symbol. If the user runs

 (attach-tag 'scheme-number 5) 

the version given in this comment will return 5, just like the implementations given at the top of the page.


RWitak

After much confusion on my part, I also agree that torinmr's solution is optimal for additivity. The number itself is never represented as anything other than itself, only when deciding on how to tag and how to look up the number are there 'scheme-number' tags involved - which I think is acceptable as long as the number representation stays 5 instead of (scheme-number . 5).


jirf

Weighing in on the great number? debate. The purpose of this exercise is to make the system "work as before except that ordinary numbers should be represented simply as Scheme numbers rather than as pairs whose car is the symbol scheme-number".

To do this all we have to do is create a type dispatch in our type system that recognizes when the object is a number and returns the 'scheme-number tag that was never there in order to route the arithmetic procedures (add, sub, div, mul) to the correct procedures.

 (define (attach-tag t x) 
   (if (number? x) x (cons t x))) 
 (define (type-content x) 
   (cond 
     ((pair? x) 
      (if (symbol? (car x) x) 
          (error "Invalid Type Tag. Tag Must Be Symbol Not " (car x))) 
      ((number? x) (cons 'scheme-number x)) 
      (else (error "Unrecognizable Type For" x))))) 
 (define (type-tag x) 
   (car (type-content x))) 
 (define (contents x) 
   (cdr (type-content x))) 

partj

In addition to the changes to the type-tag system, we can also now simplify the scheme-number package definitions (because we don't need to tag a scheme number anymore):

 (define (install-scheme-number-package) 
   (put 'add '(scheme-number scheme-number) +) 
   (put 'sub '(scheme-number scheme-number) -) 
   (put 'mul '(scheme-number scheme-number) *) 
   (put 'div '(scheme-number scheme-number) /) 
   'done) 

The only thing required is that querying (type-tag some-scheme-number) should return 'scheme-number'.