Question

I finally started learning functional languages (emacs lisp) and it makes explicit distinction between functions and special forms such as flow control , for example if.

Is there a fundamental/theoretical reason why special forms are distinct from functions? do any languages provide functional if?

Thanks

Was it helpful?

Solution

With eager evaluation the distinction is required, languages with lazy evaluation (i.e. Haskell) if et al. can be functions.

Eager evaluation: The function's arguments are evaluated before calling the function, and only the results are passed to the function.

Lazy evaluation: A function's arguments evaluated if and only if they are accessed.

OTHER TIPS

If if was a normal function, then both its arguments—the then form and the else form—would both be evaluated before calling the if function, because that's the rule of function evaluation: evaluate all arguments to produce values, then provide that sequence of values as arguments to the function designated by the first symbol in the list.

Instead, with if what you want to do is evaluate exactly one of the then form and else form, not both. In order to suppress evaluation of one or the other, you need either a macro or a special form.

In languages like Emacs Lisp and Common Lisp, special forms are built-in language constructs. They have different evaluation rules that normal function calls. For normal function calls all arguments are evaluated. So, you can't write an IF as a normal function - the condition determines which clause gets evaluated. Also usually you can't write your own special forms - in Common Lisp there is no language construct for defining a special form (though individual implementations must have implemented the existing ones somehow. This leads to macros. With macros you can write a syntactic transformation that transforms one expression into another one. To be able to write IF as a macro, you need to have another conditional form, which you can use for the transformed code. Lisp provides conditionals as basic constructs. Let's assume COND is such a basic construct, then you could expand IF into a usage of COND.

MY-IF as a macro in Common Lisp:

(defmacro my-if (condition true-clause false-clause)
   `(cond (,condition ,true-clause)
          (t ,false-clause)))

So

(my-if (foo-p) 'one 'two)

gets expanded into

(cond ((foo-p) 'one)
      (t 'two))

For completeness: there are no special forms in the Pico language for example, and if is a primitive function while Pico is inspired by Scheme and has eager evaluation by default.

In Scheme you could write

(define (true t f)
  (t))

(define (false t f)
  (f))

(define (function_if c t e)
  (c t e))

and then

(function_if true (lambda () 'true) (lambda () 'false))
==> true

What makes this manageable in Pico is that you can define functional parameters that take functional arguments that are "automatically" delayed. This means that you don't have to do the wrapping inside lambdas yourself. Pico therefore has eager evaluation but with lazy evaluation on demand, bypassing the need for special forms.

So, in Scheme syntax with functional parameters you can encode booleans as:

(define (true (t) (f))
  (t))

(define (false (t) (f))
  (f))

Then function if becomes:

(define (function_if c (t) (e))
  (c (t) (e)))

and

(function_if true 'true 'false)
==> true

As another example, the definition of the function and is (define (and p (q)) (p (q) false)).

Similarly you can define or, not, while, for, ... as functions, using the above encoding of booleans.

Short answer: No.

Long(er) answer: (if ...) requires that you control the evaluation order of the arguments. Lisp, being an eager language cannot do this in a function.

Workaround: do it in a macro:

(defmacro _if (cnd true false)
    (let (  (gcond (gensym))
            (gresp (gensym)))
        `(let ( (,gcond ,cnd) ;`#quotes
                (,gresp nil))        
            (and ,gcond       (setf ,gresp (multiple-value-list ,true)))
            (and (not ,gcond) (setf ,gresp (multiple-value-list ,false)))
            (values-list ,gresp))))

For example:

[dsm@localhost:~]$ clisp -q
[1]> (defmacro _if (cnd true false)
    (let (  (gcond (gensym))
            (gresp (gensym)))
        `(let ( (,gcond ,cnd) ;`#quotes
                (,gresp nil))        
            (and ,gcond       (setf ,gresp (multiple-value-list ,true)))
            (and (not ,gcond) (setf ,gresp (multiple-value-list ,false)))
            (values-list ,gresp))))
_IF
[2]> (_if (= 1 1) (+ 2 3) "bar")
5
[3]> (_if (= 1 2) (+ 2 3) "bar")
"bar"
[4]> 

In Scala it's possible to model if with correct side-effect evaluation using call-by-name arguments.

def If[A](cond : Boolean, truePart : => A, falsePart : => A) = if (cond) truePart else falsePart

These feature can be used to model lots of new control structures as well.

IF could be a function in a functional language having call-by-name semantics (lazy evaluation), as in Lambda Calculus or Algol. In fact that is, I think, at the heart of the relationship between Turing Machines and Lambda Calculus as equivalent foundations for computing. However, in languages having side-effects (like assignments to variables) it is not much use, because when things happen is important.

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