[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