1

I apologize in advance for lacking some vocabulary here. Going through Structure and Interpretation of Computer Programs, there is an early reference to 'combinations whose operators are themselves compound expressions', e.g.:

(define (a-plus-abs-b a b)
  ((if (> b 0) + -) a b))

where the combination (if (> b 0) + -) is evaluated as either a + or -, which is then evaluated as e.g. (+ a b) if b is greater than zero.

My question is: Is this different from a variable function (e.g. in PHP), and how so? Also, do variable functions and this functionality differ from using Javascript-style object references?

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
sinaraheneba
  • 781
  • 4
  • 18

3 Answers3

3

The important notion here, from the Lisp perspective, is whether a language is a 'lisp-1' or a 'lisp-2'. The difference between these two is: in an expression which looks like a function application, how is the thing which will represent the function to be applied evaluated?

So, in Lisps something which looks like a function application looks like a list:

(f a b ...)

When considering such a thing the system has to do three things:

  1. establish that it is a function application (so, for instance, (if ...) is not, and neither is a form which is a macro);
  2. if it is, evaluate the function and its arguments;
  3. call the function, passing it the result of evaluating its arguments.

(Note: I have assumed what is called applicative-order evaluation here: a normal-order language does things differently.)

And the question is how (2) happens.

  • A lisp-1 evaluates the function position (the first position) in such a form in exactly the same way as all the other positions. Thus the function position can contain a completely arbitrary expression. So ((if add + -) 1 2) is perfectly fine, as is (let ((op ...)) (op ...)) for instance.
  • A lisp-2 uses some special rule for the function position, and in particular for a lisp-2 the 'function value' of a symbol lives in a different namespace to the ordinary value. This means that what is allowed there is not a general expression, but rather whatever the rules allow. So, for instance, in a lisp-2, something like this does not work: (let ((op (lambda ...))) (op ...)), but something like this does work: (let ((car ...)) (car car)).

Famously, Scheme is a lisp-1, Common Lisp is a lisp-2.

So then the way to answer your question, I think, is to realise that this notion can be applied perfectly well to other languages.

  • JavaScript is a lisp-1, so the function position of things that look like function applications is just evaluated perfectly normally.
  • Python is a lisp-1.
  • I don't know what PHP is.

Note that in these languages the way you know that something is a function application is completely different than it is in Lisp, but the important question is, once you do know that, how do you evaluate the function position?

coredump
  • 37,664
  • 5
  • 43
  • 77
  • wouldn't we need to `funcall` that `let`-defined `car`? – Will Ness Jul 25 '19 at 18:38
  • @WillNess: We would if we wanted to call it, but I want to call the ordinary `car` function. Obviously this would be awful code in practice: I confess to things like `(destructuring-bind (first . rest) ... ...)` but I'd then try to avoid saying `(first first)` in the body, because it's just gratuitously confusing. –  Jul 25 '19 at 19:52
  • ah, so you meant that let-defined `car` to be some data, not a function... – Will Ness Jul 25 '19 at 20:07
2

In Scheme (which is used by SICP) one can use variables in function position.

First we try to use the symbol + as a function:

Trying to use a symbol as a function

> (define foo '+)
> foo
+
> (foo 1 2)
Exception: attempt to apply non-procedure +

Unfortunately this does not work: foo evaluates to the symbol + and this is itself not a function

Using a function object

But this works:

> (define foo +)
> (foo 1 2)
3

Here foo was set to the value of +, which is a function object. The foo will also evaluate to that object. Thus (foo 1 2) evaluates to 3.

Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
2

All functions in JavaScript are variables and it allows expressions in operator position:

function plus (a, b) {
  return a + b;
}

plus; // => [Function: plus]

function minus (a, b) {
  return a - b
}

minus; // => [Function: minus]

function plusAbs (a, b) {
  return (b > 0 ? plus : minus)(a, b);
}

plusAbs(3, -3); // => 6
plusAbs(3, 3);  // => 6

var plus = 4;
plus; // => 4 (no longer a function)
plusAbs(3, 3);  // ERROR! 4 is not a function

Just as Scheme it doesn't evaluate to plus or minus but rather what plus and minus evaluates to. Eg. the [function minus] and not the variable.

Functions in PHP cannot be done this way by name, but variables bound to functions:

function plusAbs ($a, $b) { 
  $add = function($a, $b) { return $a+$b; }; 
  $sub = function($a, $b) { return $a-$b; }; 
  return ($b < 0 ? $sub : $add)($a, $b); 
}
plusAbs(3, -3); // => 6

Closest you get with named functions:

function add($a, $b) { return $a+$b; }
function sub($a, $b) { return $a-$b; }

function plusAbs ($a, $b) { 
  return call_user_func($b < 0 ? 'sub' : 'add', $a, $b); 
}
plusAbs(3, -3); // => 6

Note that this no longer has expression in operator position.

Sylwester
  • 47,942
  • 4
  • 47
  • 79