My code signals the error "application: not a procedure" or "call to non procedure"
Asked Answered
H

1

7

During the execution of my code I get the following errors in the different Scheme implementations:

Racket:

application: not a procedure;
 expected a procedure that can be applied to arguments
  given: '(1 2 3)
  arguments...:

Ikarus:

Unhandled exception
 Condition components:
   1. &assertion
   2. &who: apply
   3. &message: "not a procedure"
   4. &irritants: ((1 2 3))

Chicken:

Error: call of non-procedure: (1 2 3)

Gambit:

*** ERROR IN (console)@2.1 -- Operator is not a PROCEDURE
((1 2 3) 4)

MIT Scheme:

;The object (1 2 3) is not applicable.
;To continue, call RESTART with an option number:
; (RESTART 2) => Specify a procedure to use in its place.
; (RESTART 1) => Return to read-eval-print level 1.

Chez Scheme:

Exception: attempt to apply non-procedure (1 2 3)
Type (debug) to enter the debugger.

Guile:

ERROR: In procedure (1 2 3):
ERROR: Wrong type to apply: (1 2 3)

Chibi:

ERROR in final-resumer: non procedure application: (1 2 3)
Hootchykootchy answered 2/1, 2018 at 17:0 Comment(4)
@LeifAndersen I actually used (test 4) where test was bound to (1 2 3), but opted not to include it because its not really important besides it not being a procedure :-)Hootchykootchy
Oh, okay, I'll undo that edit. :)Hauser
Hey puddy-cat! I saw you gave a fulsome answer here. Unfortunately the question author is an ingrate, and has deleted their post, taking your answer with it. Is it worth our re-posting the question again, so your answer can be re-posted? I don't know anything about Scheme, so I can't tell if this will be useful to future readers.Stodge
OK, thanks - not a holiday here in the UK, but it's the weekend tomorrow, so that'll do fine. Happy holidays and weekend to you!Stodge
H
14

Why is it happening

Scheme procedure/function calls look like this:

(operator operand ...)

Both operator and operands can be variables like test and + that evaluate to different values. For a procedure call to work, the operator value must be a procedure. From the error message it seems likely that test is not a procedure but the list (1 2 3).

Operators and operands can be expressions, too, so something like ((proc1 4) 5) is valid syntax and it is expected that the call (proc1 4) returns a procedure that is then called with 5 as its sole argument.

Common mistakes that produces these errors.

Trying to group expressions or create a block

(if (< a b)
    ((proc1)
     (proc2))
    #f)
 

When the predicate/test is true Scheme assumes will try to evaluate both (proc1) and (proc2) then it will call the result of (proc1) because of the parentheses. To create a block in Scheme you use begin:

(if (< a b)
    (begin 
      (proc1)
      (proc2))
    #f)

In this (proc1) is called just for effect and the result of teh form will be the result of the last expression (proc2).

Shadowing procedures

(define (test list)
  (list (cdr list) (car list)))

Here the parameter is called list which makes the procedure list unavailable for the duration of the call. One variable can only be either a procedure or a different value in Scheme and the closest binding is the one that you get in both operator and operand position. This would be a typical mistake made by common-lispers since in CL they can use list as an argument without messing with the function list.

wrapping variables in cond

(define test #t) ; this might be result of a procedure

(cond 
  ((< 5 4) result1)
  ((test) result2)
  (else result3))

While besides the predicate expression (< 5 4) (test) looks correct since it is a value that is checked for thurthness it has more in common with the else term and whould be written like this:

(cond 
  ((< 5 4) result1)
  (test result2)
  (else result3))

A procedure that should return a procedure doesn't always

Since Scheme doesn't enforce return type your procedure can return a procedure in one situation and a non procedure value in another.

(define (test v)
  (if (> v 4) 
      (lambda (g) (* v g))
      '(1 2 3)))

((test 5) 10) ; ==> 50
((test 4) 10) ; ERROR! application: not a procedure

Undefined values like #<void>, #!void, #<undef>, and #<unspecified>

These are usually values returned by mutating forms like set!, set-car!, set-cdr!, define.

(define (test x)
  ((set! f x) 5))

(test (lambda (x) (* x x)))

The result of this code is undetermined since set! can return any value and I know some scheme implementations like MIT Scheme actually return the bound value or the original value and the result would be 25 or 10, but in many implementations you get a constant value like #<void> and since it is not a procedure you get the same error. Relying on one implementations method of using under specification makes gives you non portable code.

Passing arguments in wrong order

Imagine you have a fucntion like this:

(define (double v f)
  (f (f v)))

(double 10 (lambda (v) (* v v))) ; ==> 10000

If you by error swapped the arguments:

(double (lambda (v) (* v v)) 10) ; ERROR: 10 is not a procedure

In higher order functions such as fold and map not passing the arguments in the correct order will produce a similar error.

Trying to apply as in Algol derived languages

In algol languages, like JavaScript and C++, when trying to apply fun with argument arg it looks like:

fun(arg)

This gets interpreted as two separate expressions in Scheme:

fun   ; ==> valuates to a procedure object
(arg) ; ==> call arg with no arguments

The correct way to apply fun with arg as argument is:

(fun arg)

Superfluous parentheses

This is the general "catch all" other errors. Code like ((+ 4 5)) will not work in Scheme since each set of parentheses in this expression is a procedure call. You simply cannot add as many as you like and thus you need to keep it (+ 4 5).

Why allow these errors to happen?

Expressions in operator position and allow to call variables as library functions gives expressive powers to the language. These are features you will love having when you have become used to it.

Here is an example of abs:

(define (abs x)
  ((if (< x 0) - values) x))

This switched between doing (- x) and (values x) (identity that returns its argument) and as you can see it calls the result of an expression. Here is an example of copy-list using cps:

(define (copy-list lst)
  (define (helper lst k)
    (if (null? lst)
        (k '())
        (helper (cdr lst)
                (lambda (res) (k (cons (car lst) res))))))
  (helper lst values))

Notice that k is a variable that we pass a function and that it is called as a function. If we passed anything else than a fucntion there you would get the same error.

Is this unique to Scheme?

Not at all. All languages with one namespace that can pass functions as arguments will have similar challenges. Below is some JavaScript code with similar issues:

function double (f, v) {
  return f(f(v));
}

double(v => v * v, 10); // ==> 10000
double(10, v => v * v);
; TypeError: f is not a function
;     at double (repl:2:10)

// similar to having extra parentheses 
function test (v) {
  return v;
}

test(5)(6); // == TypeError: test(...) is not a function

// But it works if it's designed to return a function:
function test2 (v) {
  return v2 => v2 + v;
}

test2(5)(6); // ==> 11
Hootchykootchy answered 2/1, 2018 at 17:0 Comment(3)
The function copy-list does not return a value! I do not understand enough of the procedure "value" to correct it.Simaroubaceous
@GeorgFuss In a single argument call it works the same as the identity function (lambda (v) v). Which implementation of Scheme have a non working values?Hootchykootchy
Awesome work! One of the best answers I've ever seen.Barrio

© 2022 - 2024 — McMap. All rights reserved.