[comp.lang.scheme] Logic does not apply

STCS8004%IRUCCVAX.UCC.IE@mitvma.mit.EDU (04/30/91)

The title says it all, but some elaboration is perhaps in order!

One might expect (naively?) that

     (apply bin-op (list item1 item2))  =  (bin-op  item1 item2)

for *all* bin-ops, provided that bin-op's arguments are compatible with it.
But it is not so, since instead of the expected

     (apply and '(#f #t)) --> #f

one gets some  grumble that either 'and' is an  undefined variable or it is
not the right kind of parameter-1 to 'apply'.

However if one uses the eta-conversion  (lambda (x y) (f x y)) = f then
'apply' works as expected:

     (apply (lambda (x y) (and x y)) '(#f #t)) --> #f.

The  catch  is  that  eta-conversion  is  normally  but  not  strictly (pun
*intended*) valid  for 'and'. Why? Because  the lambda-abstracted form will
evaluate *both* arguments before passing them  on to the 'and' in its body,
whereas (and a b) will not evaluate 'b' if 'a' evaluates to false (see RxRS
Section 7.3).

Now '+', being a built-in procedure, can be applied directly in expressions
like:

     (apply + '( 2 3)) --> 5

so why not 'and' and 'or' without the lambda wrapping?

It  seems to  me that  the difference  lies in  the fact  that the  logical
operators are  defined in the R3  and R3.99 reports (section  7.3) as being
*derived* expression types, so presumably do not qualify as 'procedures' in
the sense specified for the first argument to 'apply' (section 6.9).

So,  we  can  define  operators  like  'and',  'or',  ... using primitives,
therefore they  only rank as  *derived expressions*, not  procedures. Fine!
But  we can  also define  arithmetic operators  in terms  of the primitives
zero and  the successor function. Even  better, everything---boolean values
and functions,  integers and arithmetic  operators, and so  on---all can be
defined in terms of lambdas and  applications alone, so why not make *them*
derived expressions also? :-)

That the logical operators are derived  expressions may well be the *legal*
answer as to  why 'and' and 'or' cannot be  used with 'apply'---my question
is though: is it a *reasonable* answer, with or without the RxRs Reports to
hand?  Why shouldn't 'and' and 'or' be first class citizens of Scheme?

It seems that logic does not apply! :-)

Gordon Oulsnam                                 stcs8004@iruccvax.ucc.hea.ie

"When *I* use a word," Humpty Dumpty said, in a rather scornful tone,
"it means just what I choose it to mean---neither more nor less."

'Through the Looking Glass', Lewis Carroll

doug@snitor.uucp (Doug Moen) (04/30/91)

>That the logical operators are derived  expressions may well be the *legal*
>answer as to  why 'and' and 'or' cannot be  used with 'apply'---my question
>is though: is it a *reasonable* answer, with or without the RxRs Reports to
>hand?  Why shouldn't 'and' and 'or' be first class citizens of Scheme?
>
>It seems that logic does not apply! :-)
>
>Gordon Oulsnam                                 stcs8004@iruccvax.ucc.hea.ie

Good question.  If Scheme were changed to use normal order evaluation,
then `and' and `or' would indeed become first class procedures.  Also,
we could get rid of `delay' and `force', it would become easier to
define lazy lists, etc.  This increase in expressive power is not without
cost:  there is a performance penalty, and the new dialect `Lazy Scheme'
would not be backward compatible with many existing Scheme programs.
Also, the Algol-60 experience indicates that the interaction between
call by name and side effects makes imperative programs harder to understand
(think of Jensen's Device), and Scheme is, after all, an imperative language
with functional tendencies, rather than the other way around.
If we further change Scheme to make it both lazy *and* functional,
then I think we would end up with a worthy and interesting research project.
This new language would differ from the current crop of lazy functional
languages in some interesting ways: it would have latent types, rather
than strong typing, and it would would have Scheme syntax and macros.
-- 
Doug Moen | doug@snitor.uucp | uunet!snitor!doug | doug.tor@sni.de (Europe)

jaffer@zurich.ai.mit.edu (Aubrey Jaffer) (04/30/91)

> One might expect (naively?) that
> 
>      (apply bin-op (list item1 item2))  =  (bin-op  item1 item2)
> 
> for *all* bin-ops, provided that bin-op's arguments are compatible with it.
> But it is not so, since instead of the expected
> 
>      (apply and '(#f #t)) --> #f
> 
> one gets some  grumble that either 'and' is an  undefined variable or it is
> not the right kind of parameter-1 to 'apply'.
 
The problem you are having here is that `and' and `or' are poorly
named control structures.  What you want are `and?' and `or?' anyway
since tests are supposed to end with `?'.

(define (and? . args)
        (cond ((null? args) #t)
              ((car args) (apply and? (cdr args)))
              (else #f)))

(define (or? . args)
        (cond ((null? args) #f)
              ((car args) #t)
              (else (apply or? (cdr args)))))

kend@data.UUCP (Ken Dickey) (05/02/91)

STCS8004%IRUCCVAX.UCC.IE@mitvma.mit.EDU writes:

>The title says it all, but some elaboration is perhaps in order!

>One might expect (naively?) that
>     (apply bin-op (list item1 item2))  =  (bin-op  item1 item2)
>for *all* bin-ops, provided that bin-op's arguments are compatible with it.
>But it is not so, since instead of the expected
>     (apply and '(#f #t)) --> #f
>one gets some  grumble that either 'and' is an  undefined variable or it is
>not the right kind of parameter-1 to 'apply'.

I suggest that the problem is not one of "logic", but of using a similar syntax
to express both procedure calls and evaluation rules.  {The number of special
forms in Scheme is, after all, quite small}.

From your comments, you would expect the following to "work":
	(apply if    (list a b c))
	(apply begin (list a b c))
	(apply set!  (list a b))
	(apply let   (list ((a 1)(b 2) (+ a b))))
	...

(apply a b) is *not* equivalent to (eval (cons 'a b)) because of
differences in order of evaluation.


>  Why shouldn't 'and' and 'or' be first class citizens of Scheme?

Why are special forms not applications?  Because they are very useful
is expressing another level of computational specification: how
evaluation is to proceed.


-Ken Dickey					kend@data.uucp

dak@sq.sq.com (David A Keldsen) (05/03/91)

Gordon Oulsnam (stcs8004@iruccvax.ucc.hea.ie) writes:

[why aren't 'and' and 'or' strict in Scheme?]
>It  seems to  me that  the difference  lies in  the fact  that the  logical
>operators are  defined in the R3  and R3.99 reports (section  7.3) as being
>*derived* expression types, so presumably do not qualify as 'procedures' in
>the sense specified for the first argument to 'apply' (section 6.9).

Correct, although I'd put it more simply:  'and' and 'or' are syntax, not
procedures.  All Scheme procedures are strict.  'and' and 'or' are not
strict, which is how "short-circuiting" boolean evaluation is made available
to you in Scheme.

>That the logical operators are derived  expressions may well be the *legal*
>answer as to  why 'and' and 'or' cannot be  used with 'apply'---my question
>is though: is it a *reasonable* answer, with or without the RxRs Reports to
>hand?  Why shouldn't 'and' and 'or' be first class citizens of Scheme?

Because they are syntax, not procedures.  You can only apply procedures.

>It seems that logic does not apply! :-)

As you point out, if you want the strict behavior, you can get it by the
usual eta transformation; going the other way is much more difficult.
In this sense, scheme is providing you with the "more fundamental" tools.

Regards,
Dak
-- 
David A. 'Dak' Keldsen of SoftQuad, Inc. email: dak@sq.com  phone: 416-963-8337
"I daresay you haven't had much practice," said the Queen.  "When I was your 
age, I always did it for half-an-hour a day.  Why, sometimes I've believed as 
many as six impossible things before breakfast." _Through the Looking Glass_

STCS8004%IRUCCVAX.UCC.IE@mitvma.mit.EDU (05/03/91)

Summary

    A collective response to all direct  and net mailings regarding what is
    (not) a  Scheme function is  given. (Individual replies  have also been
    sent---but some replies bounced.)

    Sytactic sugar hides normal-order(-like) evaluations in Scheme.

    A plea is entered for introducing optional normal order evaluation.

Perceptions of Functionality

Many people  have made the point,  in various ways, that  if an operator, f
say, is  such that the evaluation  (reduction) of an expression  such as (f
<arg1> ...) is done using applicative order (alias reduce inner-most redexs
first, alias evaluate the arguments first) then f is a function.

But the use  of any other reduction strategy, such  as reduce the left-most
outer-most redex  first (alias normal order  reduction---described to me in
terms like  'evaluate the arguments left  to right but with  the ability to
short-circuit  later arguments  if a  result can  be returned without using
them') denies  f the status  of being a  function (in Scheme---agreed)  and
makes it instead a derived expression, special form, control structure, ...
or whatever.

Aubrey  Jaffer in  Message-Id: <JAFFER.91Apr30124710@kleph.ai.mit.edu> sees
it this way:

>  The problem you are having here is that `and' and `or' are poorly
>  named control structures.  What you want are `and?' and `or?' anyway
>  since tests are supposed to end with `?'.

>  (define (and? . args)
>          (cond ((null? args) #t)
>                ((car args) (apply and? (cdr args)))
>                (else #f)))

But I think it is much more than a matter of poor naming conventions.

All right then, is *and below a function or not when used in the expression
(*and #f arg2)?    After all, '*and' behaves like 'and':

        (define *and
           (lambda (x y)
              (cond (x (y))
                    (else #f))))

        (define arg2 (lambda ()
                      (begin
                         (display "arg2 to AND is true")
                         (newline)
                         #t)))

The answer, of course, is:- Yes!  Because y evaluates to a function (thunk)
which, ...er, ...in the example cited  ... er, ...happens not to be invoked
in deriving the answer ... . Um, yes, thank you, sounds familar!

In other  words, what 'and'  does covertly, '*and'  does overtly: both  use
(the equivalent of) normal order evaluation.

Incidentally, one can define *apply to work with *and-like functions:

      (define *apply
         (lambda (f s)
            (f (head s) (cdr s))))

and now (*apply *and (cons #f arg2)) --> #f.

(But of course (*apply + (list 2 3)) doesn't work!)


Syntactic sugar and non-applicative evaluations.

Scheme's 'and' is  simply syntactic sugar for what  in a Pascal-like syntax
could be declared as:

      (define *and (lambda  (x #/normal! y) (cond (x y) (else #f))))

Since  Pascal got  a mention,  suppose it   did not  allow the  use of  var
parameters. One would soon find it necessary to introduce two operators var
and deref, to replace the usual

      function f1 (x: <type>; var y: <type>): <type2>;
        begin ... x := y ...; ... end;
by
      function f2 (x, y: <type>): <type2>;
        begin ... x := (deref y) ...;  ... end;

and call it with:  f2 (a, (var b)), which would be  all very painful. 'Var'
is  essentially syntactic  sugar which  hides this.  (Some might recall the
l-value and r-value operators in BCPL.)

Yet the  unsugared version is  exactly the kind  of contortion one  must go
through with 'delay' and 'force' (which aren't even classed as 'essential')
if one wants to  get the effect of normal order evaluation  in Scheme. I do
not think that  PASCALers feel that f1 is any  less of a 'function' because
of  using  call-by-reference  rather  than  call-by-value.  [No,  I'm *not*
confusing    normal   order    evaluation   with   call-by-reference---just
illustrating  the fact, and value,  of intelligently  interpreted syntactic
sugar to hide non-standard evaluation strategies.]

A plea for user-option Normal Form evaluation

In terms  of lambda calculus, and  in the absence of  side-effects, we know
from the Church-Rosser theorems that *provided* the computations terminate,
the normal form  of the expression (its value) is  independent of the order
in which the reduction  steps are applied (thank goodness!)---Church-Rosser
I. We  also know that,  for a given  expression, it may  be case that  some
reduction  sequences  may  cause  non-termination  whilst others terminate.
However, if there  is a normal order form for  the expression, normal order
reduction *alone* is guaranteed to find it---Church-Rosser II.

Thus the *lambda calculus* expression ((lambda (x) 0) (/ 1 0)) evaluates to
0 using  normal order,  but does  not terminate  for applicative  order. Is
(lambda  (x)  0)  -  known  to  mathematicians  as  the zero *function* - a
function or not? If I write it myself  in Scheme, yes, but it will fail for
the example  given; but if  the implementors  build  it in for  me, name it
'zero' and arrange  to ignore the argument for  efficiency's sake, then no,
it  is a  special form!

Scheme's derived  expressions, special forms, control  structures and so on
are,  in lambda  calculus terms,  functions evaluated  in (something  like)
normal order. Since the necessity for these is acknowledged, *WHY NOT ALLOW
THE  USER*  to  specify  normal  evaluation  when  required?  The suggested
syntactic sugar  has been given above.  Introducing it will not  affect (as
far as I  can see) the efficiency of  programs that did not opt  to use it.
The  question  of  whether  full  normal  order  evaluation  (evaluate  the
arguments on every encounter) or lazy evaluation (evaluate each argument at
most  once, memoizing  the results)  should be  used can  be debated, but a
least  let  us  have  the  *OPTION*  of  using  some  form  of normal order
evaluation.

But perhaps Doug Moen in Message <1991Apr30.151352.1460@snitor.uucp> put it
better:

>  ... If Scheme were changed to use normal order evaluation,
>  then `and' and `or' would indeed become first class procedures.  Also,
>  we could get rid of `delay' and `force', it would become easier to
>  define lazy lists, etc. This increase in expressive power is not without
>  cost:  there is a performance penalty, and the new dialect `Lazy Scheme'
>  would not be backward compatible with many existing Scheme programs.

But I think my suggestion of  optional normal order might overcome this. As
I recall it,  there was no  penalty in ALGOL  60 for the  inefficiencies of
call-by-name if one chose not to use it.

>  ...
>  If we further change Scheme to  make it both lazy *and* functional, then
>  I think we would end up with  a worthy and interesting research project.
>  This new language would differ from  the current crop of lazy functional
>  languages in some  interesting ways: it would have  latent types, rather
>  than strong typing, and it would would have Scheme syntax and macros.

>  Doug  Moen  |  doug@snitor.uucp  |  uunet!snitor!doug  | doug.tor@sni.de
>  (Europe)

I fully agree!

Gordon Oulsnam    stcs8004@iruccvax.ucc.hea.ie

gateley@rice.edu (John Gateley) (05/03/91)

In article <9105031031.aa16176@mc.lcs.mit.edu> Gordon Oulsnam writes:
   Scheme's derived  expressions, special forms, control  structures and so on
   are,  in lambda  calculus terms,  functions evaluated  in (something  like)
   normal order. Since the necessity for these is acknowledged, *WHY NOT ALLOW
   THE  USER*  to  specify  normal  evaluation  when  required?

Just out of curiosity, how would you compile the following function:
(define foo
  (lambda (f x y z)
    (f z y x)))
Consider (foo + 1 2 3) and (foo if else then pred)?
The arguments to ANY application must be assumed to be passed in
normal order unless you can prove otherwise. Is this what you really
want? I know - I know - the efficiency of an implementation shouldn't
matter during language design, but the implementor in me feels
otherwise. 

John
gateley@rice.edu


--
"I've thought the thoughts of little children and the thoughts of men
 I've thought the thoughts of stupid people who have never been
 so much in love as they should be and got confused too easily
 to fall in love again." The Residents and Renaldo and the Loaf