From www@deja.com Thu Aug 19 15:28:16 1999 From: oleg@pobox.com To: oleg@pobox.com Subject: 'if' as a _function_, in Scheme and Python Date: Thu, 19 Aug 1999 22:31:49 GMT Reply-To: oleg@pobox.com Keywords: lazy evaluation, conditional expression, call-by-value, Scheme, Python Newsgroups: comp.lang.functional,comp.lang.scheme,comp.lang.python Organization: Deja.com - Share what you know. Learn what you don't. Summary: Implementations of 'if' as a pure function rather than a special form X-Article-Creation-Date: Thu Aug 19 22:31:49 1999 GMT Content-Length: 3111 Status: OR In most languages, 'if' has a distinguished status, of a statement or a special syntax form. One wonders whether 'if' can be a regular function. This question has a particular relevance to Python as it lacks conditional expressions (as a ternary (?:) operator in C). IF _can_ be a regular function, provided the evaluation of its 'branch' arguments could somehow be delayed. Lazy languages like Haskell are never in a hurry to evaluate arguments of a function call; in these languages, IF indeed is no different than the other functions. In eager, call-by-value languages the evaluation delay has to be spelled out explicitly. For example, in Scheme: (define (my-if condition then-branch else-branch) ((or (and condition then-branch) else-branch))) on stipulation that the arguments of this function are wrapped in lambda-expressions (i.e., thunks): (my-if (> 2 0) (lambda () (+ 2 3)) (lambda () (/ 1 0))) (my-if (pair? x) (lambda () (car x)) (lambda () #f)) BTW, in the my-if definition above, 'and', 'or' can be regular functions rather than special forms. To show it clearer, let me re-write my-if as (define (my-if condition then-branch else-branch) ((cdr (assq (eq? condition #f) (list (cons #t else-branch) (cons #f then-branch)))))) This definition is _purely_ functional: my-if is composed of regular functions only; it does not include any special forms. Surprisingly, the same technique _literally_ applies to Python. In this language, the "if function" has more than an academic interest as an 'if statement' in Python is not an expression (that is, does not yield a value). And a ternary ?: operator is absent. However, the following expression makes up for this: x = eval((lambda: exp1, lambda: exp2)[not condition].func_code,vars()) this expression is equivalent to if condition: x = exp1 else: x = exp2 No matter what object the 'condition' is or yields, "not condition" is always 0 or 1. It has the value of 1 if and only if the 'condition' evaluates to a "false object". Like the Scheme my-if function, the above Python expression is purely functional -- it does not include 'or' and similar shortcut operators. The 'if' expression appears so complex because Python lacks truly lexical scope. Still, separate local bindings can be imported into a nested function via default arguments. The trick with eval shown above is a slightly more generic version of emulating nested lexical or dynamic scopes. The eval function should not impose a big overhead as it is given an already compiled expression (code object). In this case, the eval acts as a generalized function call. Here is a more complex and real example: http.putrequest('POST', eval((lambda: "http://%s:%d%s" % (remote_host, remote_port, server_url), lambda: server_url)[not proxy_name].func_code,vars()) In this example, the conditional expression saves a temporary variable, which will otherwise be required and has to be allocated and assigned to. Conditional expressions help reduce pollution of the namespace.