Chris Jester-Young's answer is right, but there's one other point I want to highlight. The standard and
operator is a macro which delays the evaluation of its arguments, by (essentially, if not exactly) turning (and a b c)
into (if a (if b c #f) #f)
. This means that if a
is false, b
and c
do not get evaluated.
We also have the option of defining an and-function
such that (and-function a b c)
evaluates a
, b
, and c
, and returns true when the values are all true. This means that all of a
, b
, and c
get evaluated. and-function
has the nice property that you can pass it around as function because it is a function.
There's still one option that seems to be missing: an and-function-delaying-evaluation
that returns return if and only if a
, b
, and c
all return true, but that doesn't evaluate, e.g., b
and c
if a
produces false. This can be had, actually, with a function and-funcalling-function
that requires its arguments to be a list of functions. For instance:
(define (and-funcalling-function functions)
(or (null? functions)
(and ((car functions))
(and-funcalling-function (cdr functions)))))
(and-funcalling-function
(list (lambda () (even? 2))
(lambda () (odd? 3))))
; => #t
(and-funcalling-function
(list (lambda () (odd? 2))
(lambda () (even? 3)))) ; (even? 3) does not get evaluated
; => #f
Using a macro and this idiom, we can actually implement something with the standard and
semantics:
(define-syntax standard-and
(syntax-rules ()
((standard-and form ...)
(and-funcalling-function (list (lambda () form) ...)))))
(macroexpand '(standard-and (odd? 2) (even? 3)))
; =>
; (and-funcalling-function
; (list (lambda () (odd? 2))
; (lambda () (even? 3))))
The lesson to take away from this, of course, is that you can have an and
-like function that you can pass around and still get delayed evaluation; you just need to delay evaluation by wrapping things in functions and letting the and
-like function call those functions to produce values. (In Scheme, this might be an opportunity to use promises.)