In Scheme, can if be expressed as a combination of boolean operators?

382 Views Asked by At

It is easy to express and, or, and not in terms of if (with an assist from a local binding for or). I'd like to know if the reverse is true. My naïve first attempt:

(if test conseq altern) => (or (and test conseq) altern)

However, if test is non-#f and conseq is #f, the translation evaluates to altern, which is incorrect.

Is there a translation that evaluates to the correct value while maintaining the short-circuit nature of if?

3

There are 3 best solutions below

6
On BEST ANSWER

Sounds like you have a good explanation why if is doing a little more than and and or. But if you could cheat and add a lambda to delay the actual result:

(define-syntax-rule (if c t e) ((or (and c (lambda () t)) (lambda () e))))
0
On

This is the same as Eli Barzilay's answer, except instead of wrapping it in a lambda, I wrap it in a 1-element list

(if test conseq altern) => (car (or (and test (list conseq)) (list altern)))

By the way, Python prior to 2.5 had this exact problem. There was no nice way to write a conditional expression (i.e. test ? conseq : altern in C, which is what if in Scheme is) in Python. The simplest attempt was

test and conseq or altern

which worked in most cases, but failed when conseq is considered false in Python (i.e. False, 0, empty list, empty string, etc.). Which is the exact problem you discovered above. The fix was to wrap it in a list (non-empty lists are always true) and extract it again:

(test and [conseq] or [altern])[0]

which looks ugly. Which is why they added the syntax conseq if test else altern in Python 2.5.

2
On

Try (or (and test conseq) (and (not test) altern)) as the general pattern. By negating test in the second and, it ensures that the outer disjunction will be either conseq#f if test is true, or #faltern if test is false.