[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

RE: expressions vs. statements



> -----Original Message-----
> From: owner-ll1-discuss@ai.mit.edu
[mailto:owner-ll1-discuss@ai.mit.edu]
> On Behalf Of Kragen Sitaker
> Sent: Tuesday, December 18, 2001 11:10 PM
> To: ll1-discuss@ai.mit.edu
> Subject: Re: expressions vs. statements
> 
> (I wondered why nobody had responded to this post.  Then I discovered
> it was because I hadn't sent it.)
> 
> Paul Graham writes:
> > This is actually a question people on this list might
> > have interesting opinions about.  Does distinguishing
> > between expressions and statements buy you anything in
> > expressive power?  I have often wondered about what
> > the point of this distinction was...
> 
> In Python, it buys you three things; whether or not they constitute
> expressive power is debatable.  At least one of them is a deliberate
> restriction on expressive power, by some definitions.
> 
> 1. Python uses == for testing equality, like C.  People, even
> experienced programmers who have been using Python and C for many
> years, tend to forget every once in a while and write = instead.  Not
> allowing = in expressions makes this a syntax error instead of a
> semantic bug.

In SmallScript/Smalltalk:

    :=    Assignment
    =     Equality (corresponds to #hash)
    =~    Equivalence (corresponds to #equivHash)
    ==    Identity (corresponds to #identHash/#basicHash)

> 
> In general, restrictions and redundancy in syntax and semantics make
> it easier to detect programming errors and provide useful diagnostics
> for them.  The right degree of restriction and redundancy depend
> partly on the user and partly on the particular errors being caught.
> 
> 2. It means syntax that means one thing as a statement can mean
> something else in some other context; while this can lead to code
> that's harder to read, in Python it means we can use = for passing
> named parameters to functions.  I think that's actually a significant
> win in notation over using, say, : or @, because the value on the
> right side of = does in fact get bound to the name on the left in the
> called function's namespace.

    Method [
    foo(a,@b=defValExpr,@c)
        ...
    ]

    Eval [
        foo(12,@c=expr)
    ]

> 
> 3. The restriction on expressive power is that it makes
> hard-to-understand code more verbose, by attaching a syntactic penalty
> to side effects, so that people don't tend to write it as often.  For
> example, people tend to write variable definitions and increments in
> separate statements instead of burying them in some other expression.
> This arguably results in clearer code.  (This isn't much of a problem
> in Lisp dialects, because binding a variable requires a binding form
> like a let, and at least in Scheme, set! has an undefined return value
> and tends to return useless values.)
> 
> For the same reason, Python object methods that mutate things tend to
> return None, which makes it less likely for people to use them in
> expressions.

    Method [<void>
    foo
        ...
    ]

Will return <nil> rather than <self> as the default result.

> 
> But sometimes I write Python code like this test case, which
> demonstrates why I wish I had macros, inline first-class code blocks,
> or the unification of expressions and statements:
> 
>     def testBoundsChecking(self):
>         # other tests like testSetUp check bounds checking on reads
>         def lambdaSucks(self=self): self.lw[4] = 1  # lambda sucks in
> Python
>         self.failUnlessRaises(IndexError, lambdaSucks)
>         def lambdaStillSucks(self=self): self.lw[-5] = 1
>         self.failUnlessRaises(IndexError, lambdaStillSucks)
>         def lambdaSucksBad(self=self): del self.lw[4]
>         self.failUnlessRaises(IndexError, lambdaSucksBad)
>         def moreLambdaSuckage(self=self): del self.lw[-5]
>         self.failUnlessRaises(IndexError, moreLambdaSuckage)
> 
> Assignment and deletion are two operations in Python that can't be
> done in an expression, and lambda encloses expressions, not
> statements.  So I have to name the statements that should give rise to
> IndexError.  I'd really much rather say
> failUnlessRaises IndexError (lw[4] = 1).

    Method behavior: Block [
    failUnlessRaises(signal "or signals")
        try [self()] catch(signal) [^nil].
        ^FailureSignal.throw
    ]

    Eval [
        [lw[4] := 1].failUnlessRaises(IndexError). 
    ]

Or just write:

    Eval [
        try [lw[4] := 1. FailureSignal.throw] 
            catch(IndexError) [].
    ]

-- Dave S. [SmallScript LLC]

SmallScript for the AOS & .NET Platforms
David.Simmons@SmallScript.com | http://www.smallscript.org