Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #72928

Re: Uniform Function Call Syntax (UFCS)

Path csiph.com!v102.xanadu-bbs.net!xanadu-bbs.net!news.glorb.com!newsfeed.xs4all.nl!newsfeed4a.news.xs4all.nl!xs4all!newsgate.cistron.nl!newsgate.news.xs4all.nl!post.news.xs4all.nl!not-for-mail
Return-Path <ian.g.kelly@gmail.com>
X-Original-To python-list@python.org
Delivered-To python-list@mail.python.org
X-Spam-Status OK 0.000
X-Spam-Evidence '*H*': 1.00; '*S*': 0.00; 'python.': 0.02; '"this': 0.03; 'else:': 0.03; 'static': 0.04; 'argument': 0.05; 'explicitly': 0.05; '(of': 0.07; 'attribute': 0.07; 'encouraging': 0.07; 'method.': 0.07; 'statically': 0.07; 'suppose': 0.07; 'badly': 0.09; 'calls.': 0.09; 'creator': 0.09; 'exception,': 0.09; 'function:': 0.09; 'implements': 0.09; 'imported': 0.09; 'mess': 0.09; 'override': 0.09; 'raises': 0.09; 'subject:Function': 0.09; 'terms,': 0.09; 'try:': 0.09; 'typed': 0.09; 'python': 0.11; 'def': 0.12; 'language,': 0.12; '"from': 0.16; '*f*': 0.16; 'argument.': 0.16; 'attribute,': 0.16; 'behavior:': 0.16; 'cleaner': 0.16; 'concern,': 0.16; 'func': 0.16; 'function"': 0.16; 'gentle': 0.16; 'great!': 0.16; 'hasattr(x,': 0.16; 'incorrect': 0.16; 'readability': 0.16; 'scope,': 0.16; 'scope.': 0.16; 'syntax,': 0.16; 'tested)': 0.16; 'url:drdobbs': 0.16; 'exception': 0.16; 'for?': 0.16; 'sat,': 0.16; 'language': 0.16; 'wrote:': 0.18; '(not': 0.18; 'looked': 0.18; 'bit': 0.19; 'module': 0.19; 'passing': 0.19; 'resolved': 0.19; 'typing': 0.19; 'import': 0.22; 'proposed': 0.22; 'either.': 0.24; 'necessary.': 0.24; 'passes': 0.24; 'fairly': 0.24; 'fine': 0.24; '(or': 0.24; 'extension': 0.26; 'first,': 0.26; 'mention': 0.26; 'pass': 0.26; 'least': 0.26; 'defined': 0.27; 'header:In- Reply-To:1': 0.27; 'idea': 0.28; 'function': 0.29; 'feature': 0.29; 'skip:p 30': 0.29; 'addressed.': 0.29; 'am,': 0.29; 'raise': 0.29; "doesn't": 0.30; 'message-id:@mail.gmail.com': 0.30; "i'm": 0.30; 'accidentally': 0.31; 'idea,': 0.31; 'inspect': 0.31; 'keyerror:': 0.31; 'object.': 0.31; 'second,': 0.31; 'allows': 0.31; 'class': 0.32; 'probably': 0.32; 'languages': 0.32; 'skip:m 30': 0.32; 'are:': 0.33; 'not.': 0.33; 'sense': 0.34; 'skip:_ 10': 0.34; 'could': 0.34; 'subject: (': 0.35; 'except': 0.35; 'something': 0.35; 'no,': 0.35; 'but': 0.35; 'received:google.com': 0.35; 'there': 0.35; 'really': 0.36; 'false': 0.36; 'object,': 0.36; 'method': 0.36; 'should': 0.36; 'example,': 0.37; 'wrong': 0.37; 'too': 0.37; 'easily': 0.37; 'checks': 0.38; 'to:addr:python-list': 0.38; 'rather': 0.38; 'ability': 0.39; 'does': 0.39; 'bad': 0.39; 'environment.': 0.39; 'though,': 0.39; 'sure': 0.39; 'to:addr:python.org': 0.39; 'skip:p 20': 0.39; 'called': 0.40; 'even': 0.60; 'most': 0.60; 'first': 0.61; 'reach': 0.63; 'such': 0.63; 'skip:n 10': 0.64; 'our': 0.64; 'pick': 0.64; 'provide': 0.64; 'more': 0.64; 'different': 0.65; 'obvious': 0.74; 'special': 0.74; 'article': 0.77; 'confusing': 0.84; 'experiment': 0.84; 'url:2013': 0.84; 'url:27': 0.84; 'magical': 0.91; 'mistakenly': 0.91; 'before?': 0.93
DKIM-Signature v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to :content-type; bh=6BT1zNjYgch5Qxth6IGzZCrqo83x+wTlb/7VQVvkTkg=; b=YjRu2bLUgTMW+x6aKqVD0fEmw+EfkH1bBnPIxQU2ysqUC0b2iWmeLnZHaq1jgrMFou iLTDvG6nTS2cQic+xCmrROLXZuZ1bA+jxQ3H4+aJKjHil/etsMXMsr3iG7DryR+6QPO/ 4w8XUMa4ip70gpOJqHA6baWOO99CrgNuUsSp/ie6zGRt1DaJD5nHr3fK9oiLU+9K4xWo tKpNsOvgjo0IPNtGl00MfBU4vYkxKl9vSDmoxZmuq1vmuZ+PD33T916r8S+K3qJA+vHI nImLub/CkpquSFkJBISYnKGBzrkidnEE4l/yHXFlXIJr2gNyTxzLEVnAU9vOQx1tP7W9 eo1A==
X-Received by 10.236.157.40 with SMTP id n28mr1145262yhk.29.1402168888676; Sat, 07 Jun 2014 12:21:28 -0700 (PDT)
MIME-Version 1.0
In-Reply-To <8b96ae27-20fa-4df9-807e-c806fed983c0@googlegroups.com>
References <8b96ae27-20fa-4df9-807e-c806fed983c0@googlegroups.com>
From Ian Kelly <ian.g.kelly@gmail.com>
Date Sat, 7 Jun 2014 13:20:48 -0600
Subject Re: Uniform Function Call Syntax (UFCS)
To Python <python-list@python.org>
Content-Type text/plain; charset=UTF-8
X-BeenThere python-list@python.org
X-Mailman-Version 2.1.15
Precedence list
List-Id General discussion list for the Python programming language <python-list.python.org>
List-Unsubscribe <https://mail.python.org/mailman/options/python-list>, <mailto:python-list-request@python.org?subject=unsubscribe>
List-Archive <http://mail.python.org/pipermail/python-list/>
List-Post <mailto:python-list@python.org>
List-Help <mailto:python-list-request@python.org?subject=help>
List-Subscribe <https://mail.python.org/mailman/listinfo/python-list>, <mailto:python-list-request@python.org?subject=subscribe>
Newsgroups comp.lang.python
Message-ID <mailman.10859.1402169265.18130.python-list@python.org> (permalink)
Lines 102
NNTP-Posting-Host 2001:888:2000:d::a6
X-Trace 1402169265 news.xs4all.nl 2831 [2001:888:2000:d::a6]:46296
X-Complaints-To abuse@xs4all.nl
Xref csiph.com comp.lang.python:72928

Show key headers only | View raw


On Sat, Jun 7, 2014 at 12:45 AM, jongiddy <jongiddy@gmail.com> wrote:
> The language D has a feature called Uniform Function Call Syntax, which allows instance methods to be resolved using function calls.
>
> In Python terms, the call:
>
> x.len()
>
> would first check if 'x' has a method 'len', and would then look for a function 'len', passing 'x' as the first argument.
>
> The big wins are:
>
> - the ability to override functions with more optimal class-specific implementations. (Of course, len() is a bad example, since we already have a way to override it, but there are other functions that do not have a special method).
>
> - the readability of a.b().c().d() vs c(a.b()).d()
>
> Here's a few links discussing the feature in D:
> - First, a fairly gentle "this is cool" post: http://www.kr41.net/2013/08/27/uniform_function_call_syntax_in_d.html
> - Second, an article from the Walter Bright, the creator of D: http://www.drdobbs.com/cpp/uniform-function-call-syntax/232700394
>
> Has this been discussed or proposed before? I found PEP's 443 and 3124, which provide a form of function overloading, but not reordering.

It's a nice feature in a statically typed language, but I'm not sure
how well it would work in a language as dynamic as Python.  There are
some questions that would need to be addressed.

1) Where should the function (or perhaps callable) be looked for?  The
most obvious place is the global scope.  I think it would be a bit too
far-reaching and inconsistent with other language features to reach
directly inside imported modules (not to mention that it could easily
get to be far too slow in a module with lots of imports). As a result
it would have to be imported using the "from module import function"
syntax, rather than the somewhat cleaner "import module" syntax.
While there's nothing wrong with such imports, I'm not sure I like the
thought of the language encouraging them any more than necessary.

Probably local (and by extension nonlocal) scoping is fine also.  This
makes perfect sense to me:

    def some_function(x):
        def my_local_extension_method(self): return 42
        print(x.my_local_extension_method())

2) What about getattr and hasattr?  If I call hasattr(x,
"some_method"), and x has no such attribute, but there is a function
in the global scope named "some_method", should it return True?  I
think the answer is no, because that could mess with duck typing.  Say
I have a function that checks the methods of some object that was
passed in, and it then passes that object on to some other function:

    def gatekeeper_for_f(x):
        # f behaves badly if passed an x without a key_func,
        # so verify that it has one.
        if not hasattr(x, 'key_func'):
            raise TypeError("x has no key_func")
        else:
            return f(x)

Okay, so suppose we pass in to gatekeeper_for_f a non-conformant
object, but there happens to be a key_func in our global scope, so
hasattr returns True.  Great!  gatekeeper_for_f can call x.key_func().
But that doesn't mean that *f* can call x.key_func(), if it happened
to be defined in a different global scope.

If we instead have hasattr return False though, and have getattr raise
an exception, then we have this very magical and confusing
circumstance where getattr(x, 'method') raises an exception but
x.method does not.  So I don't think that's really a good scenario
either.

Also the idea makes me nervous in the thought that an incorrect
attribute access could accidentally and somewhat randomly pick up some
object from the environment.  In statically typed languages this isn't
a huge concern, because the extension method has to take an
appropriately typed object as its first argument (and in C# it even
has to be explicitly marked as an extension method), so if you resolve
an extension method by accident, at least it will be something that
makes sense as a method.  Without the static typing you could
mistakenly pick up arbitrary functions that have nothing at all to do
with your object.

But if you want to experiment with the idea, here's a (lightly tested)
mixin that implements the behavior:

import inspect
import types

class ExtensionMethodMixin:
    def __getattr__(self, attr):
        parent_frame = inspect.currentframe().f_back
        if parent_frame:
            try:
                func = parent_frame.f_locals[attr]
            except KeyError:
                func = parent_frame.f_globals.get(attr)
            if callable(func):
                try:
                    __get__ = func.__get__
                except AttributeError:
                    return types.MethodType(func, self)
                else:
                    return __get__(self, type(self))
        return super().__getattr__(attr)

Back to comp.lang.python | Previous | NextPrevious in thread | Next in thread | Find similar | Unroll thread


Thread

Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-06 23:45 -0700
  Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-07 13:20 -0600
    Re: Uniform Function Call Syntax (UFCS) Gregory Ewing <greg.ewing@canterbury.ac.nz> - 2014-06-08 13:27 +1200
      Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 01:26 -0700
        Re: Uniform Function Call Syntax (UFCS) Paul Sokolovsky <pmiscml@gmail.com> - 2014-06-08 15:06 +0300
          Re: Uniform Function Call Syntax (UFCS) Marko Rauhamaa <marko@pacujo.net> - 2014-06-08 18:56 +0300
            Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-08 10:38 -0600
            Re: Uniform Function Call Syntax (UFCS) Paul Sokolovsky <pmiscml@gmail.com> - 2014-06-08 19:40 +0300
            Re: Uniform Function Call Syntax (UFCS) Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-06-09 04:33 +0000
              Re: Uniform Function Call Syntax (UFCS) Marko Rauhamaa <marko@pacujo.net> - 2014-06-09 09:25 +0300
                Re: Uniform Function Call Syntax (UFCS) Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-06-09 09:09 +0000
                Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-09 19:13 +1000
                Re: Uniform Function Call Syntax (UFCS) Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-06-09 13:37 +0000
                Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-10 01:08 +1000
          Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 09:24 -0700
            Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 09:34 -0700
            Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-08 10:54 -0600
            Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-09 03:10 +1000
              Re: Uniform Function Call Syntax (UFCS) Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-06-09 03:20 +0000
                Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-09 13:44 +1000
                Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 23:38 -0700
                Re: Uniform Function Call Syntax (UFCS) Roy Smith <roy@panix.com> - 2014-06-08 23:45 -0400
      Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 02:25 -0700
        Re: Uniform Function Call Syntax (UFCS) Roy Smith <roy@panix.com> - 2014-06-08 10:59 -0400
          Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 08:39 -0700
            Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-09 02:48 +1000
              Re: Uniform Function Call Syntax (UFCS) Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-06-09 03:53 +0000
                Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-09 14:53 +1000
                Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-09 08:24 -0600
                Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-09 23:43 -0700
            Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-08 11:08 -0600
            Re: Uniform Function Call Syntax (UFCS) Chris Angelico <rosuav@gmail.com> - 2014-06-09 03:13 +1000
            Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-08 11:24 -0600
              Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 13:35 -0700
    Re: Uniform Function Call Syntax (UFCS) jongiddy <jongiddy@gmail.com> - 2014-06-08 01:15 -0700
      Re: Uniform Function Call Syntax (UFCS) Paul Sokolovsky <pmiscml@gmail.com> - 2014-06-08 14:52 +0300
      Re: Uniform Function Call Syntax (UFCS) Ian Kelly <ian.g.kelly@gmail.com> - 2014-06-08 11:00 -0600

csiph-web