On Mon, Dec 6, 2021 at 5:20 AM Christopher Barker <python...@gmail.com> wrote:
>
> On Sun, Dec 5, 2021 at 3:28 AM Chris Angelico <ros...@gmail.com> wrote:
>>
>> (That said, though: it would be rather nice to be able to do algebra
>> with function signatures. For instance, you could say "my signature is
>> that function's kwargs plus frobnosticate=42" or "my signature is that
>> function's kwargs minus stdin". But that's a topic for another thread
>> or another day.)
>
>
> Heck, or even " my signature is that other function's signature" -- that is 
> what passing *args, **kwargs does, but you have to look at the implementation 
> to know.
>

Ah, if it's absolutely exactly "that other function's signature", then
set func.__wrapped__ to the other function (that's what
@functools.wraps does to make the signature work). But that's the only
option. You can't do modifications in this way:

def func(*args, **kwargs, frobnosticate=42):
    ...
    basefunc(**args, **kwargs)

def func(*args, **kwargs):
    if "stdin" in kwargs:
        stdin = kwargs.pop("stdin")
        ...
    basefunc(*args, **kwargs)

You have to do all-or-nothing at the moment. I don't know of a good
way to make this happen, but if someone has a brilliant idea, I'd love
to hear one.

> As it happens, right now, someone on my team is trying out an implementation 
> that uses inspect to grab the signature of superclass methods so that we can 
> have a complete function signature without repeating ourselves all over the 
> place. Not sure that's a good idea, but it would be cool if there were a 
> standard and reliable way to do that.
>

If you mean that it's chasing all the way up the class hierarchy,
building the signature piece by piece, then you're right, there's no
easy way to do that at the moment. Signature algebra would allow you
to do that - you'd have each function say "that function, but these
changes" - but how you specify that is the hard part.

> But yes, topic for another day.

Indeed.

>> > > None is most assuredly not going to trigger a late-bound default.
>>
>> I don't think so, because None doesn't mean "omit this argument". It
>> is a perfectly valid value. There's also no need to say that object()
>> won't trigger late-bound defaults, or 0, or anything else. The only
>> way to cause a default argument to be evaluated is to not pass the
>> argument - as is already the case.
>
>
> But I'd like to see as a (perhaps rejected) idea is to have a new sentinel 
> that does mean undefined.Sure there could be (rare) cases where you would 
> need to have it a valid value, but then maybe you can't use late-bound 
> defaults in that case.
>
> This is very different from None, because it would be new, so no one is 
> already using it for anything else. And sure, folks could choose to use it 
> inappropriately, but consenting adults and all that.

I think that would be more confusion than it's worth. Having an object
with no value is a means of major insanity. I'll give a specific
example of something that frustrated me from JavaScript; this kind of
API is very common:

// If "foo" is present, remove it. Otherwise, add it
classList.toggle("foo")
// Add "foo"
classList.toggle("foo", 1)
// Remove "foo"
classList.toggle("foo", 0)

These are defined using the standard truthiness rules: any true value
will add, any false value will remove. And if you don't pass anything
at all, it toggles.

Next up, consider this:

state = {
    "title": "some title",
    "is_open": true/false,
    "etc": etc,
}

// Check to see if the thing is open or not
if (state.is_open) {...} else {...}

Cool. Nice and easy. Both of these use the truthiness rules. Seems
pretty normal, right?

Unfortunately, there is ONE special value which doesn't behave the
same way: undefined. In an 'if' statement, undefined is falsy, so
you'd go into the 'else' clause. Just like None in Python, just like
every language with a concept of truthiness/falsiness. But in the
toggle() call, undefined is indistinguishable from not passing the
argument at all. So instead of removing, it will toggle.

That specific issue cost me some debugging time, because I didn't even
think to ask the question "why is state.is_open undefined instead of
false" - because in every other way, undefined did indeed behave as if
it were false.

>> Yeah :) I say this because, in JavaScript, there is fundamentally no
>> difference between passing the special value 'undefined' (kinda like
>> None, although there's also null as a separate value) and not passing
>> the argument at all, which means that...
>>
>> function foo(x="hello") {console.log("x is " + x);}
>> foo(undefined);
>> foo(foo.any_unknown_attr);
>>
>> will print "x is hello" twice. I don't want that :)
>
>
> Sure, that's ugly, but isn't the real problem here that  
> foo(foo.any_unknown_attr) doesn't raise an Exception? Would we have that same 
> issue in Python?

Well, true, that particular part of it is handled by that. But in
Python, you might get something from a dictionary using
foo.get("any_unknown_key") and you'll still get back None. What's
important is that in Python, instead of getting back a null value that
behaves as if you didn't pass anything at all, you get back a value
that is a real thing in and of itself. None isn't the absence of a
value - it is a value that has real meaning. (And real attributes,
though not many of them.)

> e.g., doesn't Javascript already have:
>
> foo();
> foo(foo.any_unknown_attr);
>
> lead to the same thing? whereas in Python, that would raise, yes?
>
> Or is there something special about undefined that I'm missing?
>   (sorry, I don't really "get" Javascript)

There are two halves to that second example: firstly, that
foo.any_unknown_attr is undefined rather than being an error; and
secondly, that passing undefined to a function is indistinguishable
from not passing an argument. You can get undefined from all kinds of
sources, and it's often used in places where Python would use None, so
that part isn't a major problem. The problem is that, in a function
call, it ceases to be a value, and becomes a non-value.

(And don't worry. Nobody really "gets" JavaScript. We just use it
anyway, since it's the thing that browsers are most comfortable with.)

> I personally think more standard special purpose sentinels would be a good 
> idea, though I understand the arguments made against that in previous 
> discussions. But this is a little different, because late-bound defaults are 
> a new thing, so we don't already have a body of code using None, or anything 
> else for "use the late bound default".
>

Hmm, the problem with a multitude of standard special-purpose
sentinels is that, inevitably, you need something that lets you
enumerate all standard sentinels, and also say "nothing to see here".
So you'll end up needing your own private sentinel. The standard ones
don't end up buying you much, unless they have meaning to the language
itself (as NotImplemented does).

ChrisA
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/NLV5AAIZPFMPWKHBRD2GTXO2MPBSGZLV/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to