'if' as a _function_, in Scheme and Python

oleg at pobox.com oleg at pobox.com
Thu Aug 19 18:31:49 EDT 1999


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.


Sent via Deja.com http://www.deja.com/
Share what you know. Learn what you don't.




More information about the Python-list mailing list