Mailing List Archive

'if' as a _function_, in Scheme and Python
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.
'if' as a _function_, in Scheme and Python [ In reply to ]
Consider:

(define (boolean->integer x)
(if x 1 0))

(define (if-function condition consequent-thunk alternate-thunk)
((vector-ref `#(,alternate-thunk ,consequent-thunk)
(boolean->integer condition))))

That is, `if' can well be thought of as syntactic sugar for a particularly
easy-to-optimize `case' expression.

Regards, [Ag] Andy Gaynor silver@mail.webspan.net
'if' as a _function_, in Scheme and Python [ In reply to ]
On Thu, 19 Aug 1999 22:31:49 GMT, oleg@pobox.com wrote:

>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

Not only is it purely functional, it also unconditionally evaluates
both branches (in Scheme). Therefore, it is not equivalent to IF.

-Steve
'if' as a _function_, in Scheme and Python [ In reply to ]
On Thu, 19 Aug 1999 22:31:49 GMT, oleg@pobox.com <oleg@pobox.com> wrote:
>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.
>
>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):

You should look also look at Smalltalk; all operations are implemented
with message sends, and conditionals are implemented exactly as you
describe (ie with closures). Python is more like a Smalltalk than a
Scheme (to the extent that's meaningful -- ST and Scheme are
practically duals in language space).

>x = eval((lambda: exp1, lambda: exp2)[not condition].func_code,vars())
>
>this expression is equivalent to
> if condition: x = exp1
> else: x = exp2

Wow. That's probably the best ?: emulation yet. "Best" being defined
as "makes me laugh with appreciation," as opposed to actually being
practical; the if-else construction is the only one that J. Random
Hacker is likely to understand when he comes across in actual code.


Neel
'if' as a _function_, in Scheme and Python [ In reply to ]
In article <37bc9dd6.11041486@90.0.0.40>,
Steve Schafer <pandeng@telepath.com> wrote:
>On Thu, 19 Aug 1999 22:31:49 GMT, oleg@pobox.com wrote:
>
>>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
>
>Not only is it purely functional, it also unconditionally evaluates
>both branches (in Scheme). Therefore, it is not equivalent to IF.

No. Remember that then-branch and else-branch are thunks. It doesn't
matter whether you evaluate them, they don't do anything until you *call*
them. The above expression selects one of them and then calls it (notice
the extra level of parentheses before "cdr").

The subject of a functional "if" comes up in the Scheme group every 6-9
months, I think. The above implementation is clever. A more common
implementation involves replacing #t and #f with functions:

(define (if-true then-branch else-branch)
(then-branch))
(define (if-false then-branch else-branch)
(else-branch))

Predicates return if-true or if-false instead of #t or #f, respectively (we
could define that these special values actually evaluate to these
functions). Then "if" is defined as:

(define (if condition then-branch else-branch)
(condition then-branch else-branch))

--
Barry Margolin, barmar@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
'if' as a _function_, in Scheme and Python [ In reply to ]
In comp.lang.functional Barry Margolin <barmar@bbnplanet.com> wrote:

: Remember that then-branch and else-branch are thunks. It doesn't
: matter whether you evaluate them, they don't do anything until you
: *call* them. The above expression selects one of them and then
: calls it (notice the extra level of parentheses before "cdr").

: A more common implementation involves replacing #t and #f with
: functions:

: (define (if-true then-branch else-branch)
: (then-branch))
: (define (if-false then-branch else-branch)
: (else-branch))

BTW this is exactly the way "if" is implemented in Smalltalk.

Boolean>>ifTrue: trueThunk ifFalse: falseThunk

...is a method that is redefined in Boolean's two subclasses, True and
False. For the class True, it is defined as...

True>>ifTrue: trueThunk ifFalse: falseThunk

"Return the result of the value of trueThunk"

^trueThunk value

and for class False...

False>> ifTrue: trueThunk ifFalse: falseThunk

"Return the result of the value of falseThunk"

^falseThunk value

Sending this message looks like...

(a < b) ifTrue: [#yeah] ifFalse: [#boo]

...where (a < b) evaluates to either the singleton instance of True,
called true, or the singleton instance of False, called false.

The notation [<expression>] is a thunk like (lambda () <expression>)
in Scheme. With arguments a "lambda" in Smalltalk looks like...

"This is a lambda that returns the sum of its arguments"
[ :x :y | x + y]

--
Patrick Logan patrickdlogan@home.com
'if' as a _function_, in Scheme and Python [ In reply to ]
On Fri, 20 Aug 1999 00:24:25 GMT, Barry Margolin
<barmar@bbnplanet.com> wrote:

>No. Remember that then-branch and else-branch are thunks. It doesn't
>matter whether you evaluate them, they don't do anything until you *call*
>them.

Yes, but that doesn't negate what I said--it's not equivalent to IF
(since IF doesn't require that you wrap the expressions in thunks).

Essentially, you're changing the problem to fit your solution. I guess
I don't really see any point in doing that.

-Steve
'if' as a _function_, in Scheme and Python [ In reply to ]
In article <37beeec3.31761662@90.0.0.40>,
Steve Schafer <pandeng@telepath.com> wrote:
>On Fri, 20 Aug 1999 00:24:25 GMT, Barry Margolin
><barmar@bbnplanet.com> wrote:
>
>>No. Remember that then-branch and else-branch are thunks. It doesn't
>>matter whether you evaluate them, they don't do anything until you *call*
>>them.
>
>Yes, but that doesn't negate what I said--it's not equivalent to IF
>(since IF doesn't require that you wrap the expressions in thunks).

It's not syntactically equivalent, it's another way of accomplishing the
same thing.

>Essentially, you're changing the problem to fit your solution. I guess
>I don't really see any point in doing that.

This is a discussion about language design: does "if" need to be a special
form, or can it be constructed out of other, existing primitives? The
answer is that it can, with the only special form required being "lambda".
Yes, it means that it has to be called a little differently in the
theoretical variant of Scheme that doesn't have an "if" primitive; this can
be hidden using a macro, though.

A note about the original poster's implementation, though. IIRC, it used
"assq" to select the appropriate thunk to call. Can "assq" be written
without using "if"? Although "assq" is a required language primitive, it's
a pretty high-level function and would usually be expected to be
implemented in the high-level language. My version of "if" that used the
"if-true" and "if-false" functions really doesn't depend on anything other
than "lambda" and function calling.

--
Barry Margolin, barmar@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.