The problem with "as" [was "Re: PEP 318"]

Skip Montanaro skip at
Tue Mar 23 17:07:00 CET 2004

    >> With no extension beyond the current PEP 318 proposal, you might
    >> postulate returns() and accepts() decorators:
    >> def foo(x, y) [accepts(int, float), returns(int)]:
    >> ...
    >> which extend foo() with code to enforce input and output types.
    >> Further, function attributes could be added which could be used by
    >> tools like pychecker for intermodule type checking.

    Joe> Not a big fan of that syntax - I have to keep the parameter names
    Joe> and types in sync by counting.

Here's a dummy example from the in-progress version of the PEP:

    def accepts(*types):
        def check_accepts(f):
            def new_f(*args, **kwds):
                for (a, t) in zip(args, types):
                    assert isinstance(a, t), \
                           "arg %r does not match %s" % (a,t)
                return f(*args, **kwds)
            assert len(types) == f.func_code.co_argcount
            return new_f
        return check_accepts

    def returns(type):
        def check_returns(f):
            def new_f(*args, **kwds):
                result = f(*args, **kwds)
                assert isinstance(result, type), \
                       "return value %r does not match %s" % (result,type)
                return result
            return new_f
        return check_returns

    def func1(arg1, arg2):
        return arg1 * arg2
    func1 = accepts(int, (int,float))(func1)
    func1 = returns((int,float))(func1)

    print func1(5, 3.0)
    print func1(5, 3)

    def func2(arg1, arg2):
        return str(arg1 * arg2)
    func2 = accepts(int, (int,float))(func2)
    func2 = returns((int,float))(func2)
        print func2(5, 3.0)
    except AssertionError, msg:
        print msg

    def func3(arg1, arg2, *rest, **kwds):
        r = arg1 + arg2
        print "r:", r
        print "rest:", rest
        print "kwds:", kwds
        if 'result' in kwds:
            return kwds['result']
        return r
    func3 = accepts(int, int)(func3)
    func3 = returns(int)(func3)

    print func3(3,5,'a','b','c',other="47")
    print func3(3,5,'a','b','c',result=47)

Note that check_accepts() does the counting for you.  If you wanted type
declarations in the language I agree it would be more natural for the
arguments and their types to be side-by-side, but then it wouldn't be
Python. ;-)

    Joe> For decorators in general, I like

    Joe>     def foo() as [decor1, decor2, decor3]:
    Joe> You get an explicit list syntax, but it's set off by a keyword so
    Joe> they don't run together to the eye.  Because the keyword keeps it
    Joe> unambiguous, you could even allow a tuple instead of a list: "def
    Joe> foo() as (x, y, z)".

I don't understand how

    def foo() as [decor1, decor2, decor3]:

is somehow less ambiguous than

    def foo() [decor1, decor2, decor3]:

The language parser certainly wouldn't care.

    Joe> So I definitely favour a keyword, but perhaps "as" is to generic.
    Joe> What about "has" or "with"?

I don't think any keyword adds enough to make it preferable over just a
bracketed sequence of decorators.  Python supports:

    import foo as f

The use of "as" makes perfect sense in that context.

I'd prefer prepositional keywords actually be consistent with their English
usage.  It's just line noise and is likely to provide some stumbling block
of programmers whose command of the English language is not stellar.  What
we really would say in English is

    Define function foo taking no arguments as modified by decor1, decor2
    and decor3.

No single preposition is going to read correctly as a replacement for "as
modified by", nor do we need all those other bits of English ("function",
"taking no arguments") to tell us that this is a Python function definition.
This isn't SQL or COBOL, and it's certainly not English.  My vote is to
dispense with the pseudo-keyword idea altogether.


More information about the Python-list mailing list