TypeError, the object is not applicable

2019-08-15 04:20发布

问题:

I wrote a relatively simple bank account function, however when I try to run it I get an TypeError and I'm not sure to why? It's straight out of SICP so the solution is readily available, I just want to understand why my answer produces that error. Any thoughts? Here is my code:

(define (make-password-account balance password)
    (define (withdraw amount)
        (if (>= balance amount)
            (begin (set! balance (- balance amount))
                balance)
            "Insufficient Funds"))
    (define (deposit amount)
        (set! balance (+ balance amount))
        balance)
    (define (dispatch users-guess-at-password m)
            (if (eq? users-guess-at-password password)
                (cond   ((eq? m 'withdraw) withdraw)
                        ((eq? m 'deposit) deposit)
                        (else (error "Unknown Request --make-account" m)))
               "INVALID PASSWORD"))
dispatch)

Here is the relevant interpreter inputs and outputs from the interpreter:

..loaded function...
>(define acc (make-password-account 100 'secret))
acc
>((acc 's 'withdraw) 2)
;Stack Trace:  
0  TypeError: The object "Invalid Password" is not applicable.  
1  ((acc (quote s) (quote withdraw) 2)

回答1:

Currently your code is doing this:

((acc 's 'withdraw) 2)
((dispatch 's 'withdraw) 2)
("INVALID PASSWORD" 2)
=> TypeError: The object "INVALID PASSWORD" is not applicable.

You have to handle the case where the password entered is wrong. Because you can't do anything useful with the account if the password is incorrect, you should signal an error and stop execution, for instance replacing this line:

"INVALID PASSWORD"

With this:

(error "INVALID PASSWORD")

Now the procedure will return a meaningful error message and stop execution. Alternatively, if signaling an error is too drastic, you could return a string, as suggested by @molbdnilo . Once again replace "INVALID PASSWORD" for the suggested procedure, which no matter what it receives as parameter will return the error string:

(lambda (x) "INVALID PASSWORD")

Now the execution will look like this:

((acc 's 'withdraw) 2)
((dispatch 's 'withdraw) 2)
((lambda (x) "INVALID PASSWORD") 2)
=> "INVALID PASSWORD"


回答2:

Since you're passing the wrong password,

(acc 's 'withdraw)

that is,

(dispatch 's 'withdraw)

evaluates to

"INVALID PASSWORD"

and you're attempting to apply that to the number 2, but you can't because it's not a function.

You could use a function instead of a string, similar to the other cases:

(lambda (x) "Invalid password")