Question

I discovered that special forms can not be passed as arguments or saved in variables, both in Clojure:

user=> (defn my-func 
         [op] 
         (op 1 2 3))
#'user/my-func
user=> (my-func +)
6
user=> (my-func if)
java.lang.Exception: Unable to resolve symbol: if in this context (NO_SOURCE_FILE:5)
user=> (def my-if if)
java.lang.Exception: Unable to resolve symbol: if in this context (NO_SOURCE_FILE:9)

and in Racket/Scheme:

> (define (my-func op)
    (op 1 2 3))
> (my-func +)
6
> (my-func if)
  if: bad syntax in: if
> (define my-if if)
  *if: bad syntax in: if

That's cool, I'm fine with that, I know that I can just write a function to wrap a special form and then pass that function.

But I'd like to know why Lisps have this restriction, and what negative consequences allowing this would have. Are they any major dialects that do allow that?

Was it helpful?

Solution

It makes evaluation more complicated and compilation hard.

If you have a form (a b c), then you need to resolve at runtime the value of a and then apply it somehow to the forms b and c.

The simpler model of a few limited number of special forms and otherwise strict evaluation is then gone.

See also: FEXPR

OTHER TIPS

Special forms are not functions: functions take values as arguments, whereas special forms take forms. For example, look at your example of if:

(if 1 2 3)

Okay, that's easy, since 2 and 3 are already values. But what about this?

(define (modadd a b n)
  (if (zero? n) #f (modulo (+ a b) n)))

In this case, the if is actually receiving the #f and (modulo (+ a b) n) as forms, not as values. And this is important! (modulo x n) will fail if n is 0: that is why it's left unevaluated until we know that n is not 0.

The trouble with being able to pass special forms as first-class objects is that higher-order functions cannot call those objects using predictable semantics: is it a function object, so that you're passing in values, or is it a special form, so that you're passing in forms? This gets to be a huge mess.

Yes, you can write a wrapper function, like my modadd, that encapsulates if. However, there is no way you can just reimplement if as a function, while still preserving the only-evaluate-one-of-the-branches behaviour.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top