str % vals -like idea but for statements (was Re: PEP-308 a "simplicity-first" alternative)

Beni Cherniavsky cben at techunix.technion.ac.il
Tue Feb 11 23:08:20 EST 2003


On 2003-02-11, Dan Schmidt wrote:

> I don't think I will ever fully get used to 'and' being of type
>
>   (bool, T) -> T
>
It's not.  It's (T, T) -> T!

>>> [] and 1
[]

> rather than
>
>   (bool, bool) -> bool
>
> It just doesn't read naturally to me.
>
It depends on your view on "what's a bool".  To me it's a clear win for
practicality but I never felt that it's unelegant.

> > I really dislike that type of cutesy-ness that is sometimes found in
> > Python (the % string operator is another example).
>
> Oh, I love the % operator.  I don't have an issue with it probably
> because 1) % is a funny enough character that I don't immediately
> think of 'mod' when I see it, and 2) % kinda makes sense because it is
> specifying the values of the %-expressions in the string.
>
What a handy starting point.  I have had this new idea for the last days:
I want the analog of ``str % vals`` for statements.

As Roman Suzi nicely outlined__ on python-dev, Python already has many
duplication between expressions and statements.  Most of them involve
different syntaxes and semantics to learn.  And there will always be need
of adding more.

__ http://mail.python.org/pipermail/python-dev/2003-February/033239.html

The question is, why do people want to put stements inside an expression?
After all, you can always use statements to assign a variable to the
proper value and then use it inside the expression.  I think the main
problem is not the number of lines it takes but the in-elegancy of
creating a variable to be used only once in the following expression. When
I read an expression, I see up front what will be done to the results of
every sub-expression.  When I assign a variable, there is the doubt that
it will be used later again.

In other words, the *order* obstructs the reading.  A nice property of
prefix function call notation is that you see what will be done each
result before reading how the result is constructed.  And that helps your
mind see the picture.  In many cases the details of the construction are
less interesting but until you see the use you are alarmed - "how will
this be used?".

My proposal
===========

Allow adding a class-like indented block of code to any simple statement,
with the semantics that the block is evaluated first and the statement is
the evaluated with the block's variables in scope.  Here is how you do a
conditional expression morea readably with it::

    print "Complex text about", n, things where:
        if n == 1:
            things = "thing"
        else:
            things = "things"
    print "More code..."

True, it's not more compact but it's more arguably more readable.

Another example of the same problem is the extended def syntax recently
discussed on python-dev.  Here is how my proposal helps::

    foo = syncronized(staticmethod(_)) where:
        def _(self):
            do_something_long

(it might be better to name the inner function `foo` too; see below
discussion on the scoping rules).  Or::

    bar = property(get_bar, set_bar) where:
        def get_bar(self):
            some_code
        def set_bar(self, val):
            other_code

True, this means that there are now two way to do any thing.  But these
are always the same two ways!  Besides, for most cases only one of them
will be obvious (as the more readable).  The main rule of thumb is that
the attached block should no have side effects (at least not ones whose
order of execution with respect to the statement is important). Most
importantly, it scales better than all existing shortcuts because it
allows arbitrary statements.  All existing shortcuts break at some point.

Actually I'm a bit dismayed to contemplate that if this is adopted, many
syntax extensions would be rejected because they won't add that much :-).
Don't understand me wrongly - this is a strategic solution but for many
cases we still need (at least should consider) tactical solutions that
actully make for shorter and clearer code.

Syntax
======

The above usage of ``where:`` is the clearest from I managed to find that
is based on a keyword.  ``with:``, ``for:``, ``given:`` also sound not
bad.  ``for`` has the advantage of not requiring new keywords.

However, now looking at the above code, the ``where:`` gets lost (at least
without syntax highlighting).  So I'm leaning towards a puctuation-based
syntax.  The percent sign springs to mind.  ``%%`` might look good.  Or
maybe not.  It'd look strange.  The lack of a colon is distracting.
Perhaps ``::``?

    print "Complex text about", n, things ::
        if n == 1:
            things = "thing"
        else:
            things = "things"

A single colon with no other syntax is possible but a very bad idea - it
would take away this syntax opportunity from all simple statements, at
once.

One problem inherent in the basic approach is the limitation that this
only applies to simple statements.  There is no sesnible place to poot the
block in a compund statement.

Semantics & Scoping
===================

I strongly feel that the indeented block should have a separate scope,
like a class statement.  The question is what to do with the statement
itself.  I must surely read variables from the block.  But it would be
quite useless if it would only be able to assign variables at this scope -
it should assign in the outer scope::

    x = y where:       # y readable here
        y = 2
        # y lives here
    # x lives here

If I make the statement into ``y = y``, or worse ``y += y``, complete
confusion would result.  The only solutions I see:

- Disallow assigning same variable in the statement and the block.

- Make references to the block's variables explicit.  I contemplated an
  unary ``%`` operator - it looks bad because operators never change
  varible scope in Python.  Perhaps a single dot prefix (``y = .y``)?
  Nay...  I don't know :-).  I think the first solution will win...

Ideas, comments?

-- 
Beni Cherniavsky <cben at tx.technion.ac.il>

Do not feed the Bugzillas.




More information about the Python-list mailing list