can if be a proper function rather than a special form

1k Views Asked by At

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

7

There are 7 best solutions below

3
gimpf On BEST ANSWER

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.

0
seh On

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.

13
dsm On

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]> 
0
eljenso On

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.

0
Rainer Joswig On

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))
0
Dario On

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.

0
Mike Dunlavey On

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.