
Yes, I want that when Void is received in a function parameter it is not processed: func(Void) == func() == "default"

On Mon, 25 Jul 2022 at 19:41, Михаил Крупенков <kno3.2002@gmail.com> wrote:
Yes, I want that when Void is received in a function parameter it is not processed:
func(Void) == func() == "default"
It's very difficult to follow this topic because each of your posts begins a completely new thead, with no reference, no quoted text, nothing. Are you saying that Void is a value that doesn't exist? Or that it isn't a value? Python doesn't have any concept of a "value that isn't a value", and as we've seen from SQL's NULL, there are a lot of really bizarre edge cases when you try to make that work. (NULL is a value, except when it isn't. Honestly, I don't think there's any way to describe it that is accurate, useful, and concise.) Generally, when you're carrying a function signature through *a,**kw, it's best to use functools.wraps() to help out autocomplete tools. Rather than this messy Void type (which isn't a type, and isn't a value, but somehow can be assigned to a variable), perhaps it would be more useful to show where wraps() isn't able to help you, and look into ways to solve that problem? But please, please, fix your mail client first. ChrisA

You can easily achieve this with something like Void = object() def func(param=Void): if param is Void: param = "default" or in the wrapper: Void = object() def wrapper(param=Void, **kwargs): if param is not Void: kwargs["param"] = param return func(**kwargs) With some extra work, you can even create a decorator to handle this automatically. On 25/07/2022 11:39, Михаил Крупенков wrote:

I believe this is a rebirth of a request that has come up many times before, which is to have something like javascript's `undefined` where it means "use the default value" if passed to a function that has a default value or "value not provided" (slightly different to "None").
def foo(x, y=1): ... return x, y
foo(undefined, undefined) undefined, 1
The idea being that wrapper functions don't need to then know what the default is for the function they wrap: def outermost_wrapper(b, c=undefined): return inner_wrapper(5, b, c) def inner_wrapper(a, b=undefined, c=undefined): return func(a, b, c) def func(a=1, b=2, c=3): return a*b*c "undefined" would either have to be an alias for "None" everywhere _except_ function signatures, or be only allowed in function signatures and have special handling. It opens up a can of worms in javascript but has its uses.

Mathew Elman writes:
Either would be a significant complication in Python semantics, though. And I don't really see how the latter works: notdefined = None def foo(x=undefined): # here notdefined = x return wrapped_function(x) # there foo() print(notdefined) To get this "use the default" functionality from #here to #there, either the above has to allow you to capture the object representing undefined but you're not allowed to use the name "undefined" for it (very un-Pythonic), or there has to be some horrifying magic, maybe macro-like, that turns the name "x" into a name that isn't there.
It opens up a can of worms in javascript but has its uses.
If you're phishing, I guess worms are useful. *shudder* Wisecracks aside, I'm not sure I want to read code designed so that 'undefined' is genuinely useful. I'd need to see real code that uses it, plus the translation to Python, plus some argument why it has to be that way, before I'd be willing to go higher than -1 on this complication.

To answer how this _could_ work, Undefined would be a new NoneType that is falsey (just like None) can't be reassigned (just like None) and does everything else just like None _except_ that when it is passed as a function argument, the argument name is bound to the default if it has one instead. I am not sure I understand your example
This would call wrapped_function with "undefined" so it would use the default if present, and print None because notdefined in foo is only in that scope? Did you mean to use a global notdefined in foo? In which case it would print undefined ? In your example there is no default to use... it would be this case: def foo(x=undefined): return bar(x) def bar(x=1): return x print(foo()) # 1 print(foo(2)) # 2 To be clear, I am _not_ +anything for this, I was just saying that I think this is what is really be asked for here. I am -0, I can see there being value, but that value is largely superficial and the possible can of worms is definitely a writhing one. The only problem it solves is allowing wrappers to fallback to their wrapped functions defaults, but at a fairly high cost.

On Wed, 27 Jul 2022 at 21:13, Mathew Elman <mathew.elman@ocado.com> wrote:
To answer how this _could_ work, Undefined would be a new NoneType that is falsey (just like None) can't be reassigned (just like None) and does everything else just like None _except_ that when it is passed as a function argument, the argument name is bound to the default if it has one instead.
Okay. Everything else just like None. So I can do this? a = Undefined b = {1: Undefined} c = {Undefined: 2} print(b[1]) a in c # s/be True c[a] # s/be 2 What would these do? And especially, what would happen if, instead of vanilla dictionaries, b and c were custom classes? ChrisA

I don't see why you couldn't. I guess what they do depends if any of these have defaults? Which I think they do not in this case, right? If they were non vanilla dictionaries that had a default e.g. class SomeDict(dict): def __getitem__(self, item=None): return super().__getitem__(item) def __contains__(self, item=None): return super().__contains__(item) print(b[1]) would still print Undefined because print's first argument doesn't have a default. a in c would be False. c[a] would return c[None], which would raise an error here because None isn't in the mapping Again, I am not pro this idea, just answering the questions you're asking as I see them :)

On Wed, 27 Jul 2022 at 21:54, Mathew Elman <mathew.elman@ocado.com> wrote:
So Undefined would trigger a default, but only if there is one?
print(b[1]) would still print Undefined because print's first argument doesn't have a default.
But print doesn't have a first argument - it accepts *args. So Undefined still counts as an argument, except when there's a default, at which point it counts as a non-argument?
a in c would be False.
That is VERY surprising behaviour. For literally any other object, that would be True. But then, it would be surprising in other ways if Undefined behaved like a normal object.
c[a] would return c[None], which would raise an error here because None isn't in the mapping
Again, very surprising, if putting a value into a mapping doesn't result in that value being in the mapping.
Again, I am not pro this idea, just answering the questions you're asking as I see them :)
Yeah. I think you're doing a great job of showing why this is a bad idea :) ChrisA

Chris Angelico wrote:
Yes, I guess you could do it so that for var args it was treated as a non argument? so print(Undefined) == print() ? But that seems even more surprising...
Yes, compared with other objects, there would need to be some surprising behaviour because it isn't a concept in python already. You could fix this specific problem with mappings by changing Undefined to not be hashable, but that is definitely a bandaid.
Again, I am not pro this idea, just answering the questions you're asking as I see them :) Yeah. I think you're doing a great job of showing why this is a bad idea :)
I do think the desire to fix the "wrapper not needing to know the defaults of wrapped" problem is admirable. Is this the right approach? No, I think not.

On Wed, 27 Jul 2022 at 22:54, Mathew Elman <mathew.elman@ocado.com> wrote:
Wrapper not needing to know the defaults of the wrapped can be solved with *a,**kw. This modifies the problem into "callers of the wrapper now don't know anything about the signature". I'd rather look into ways of solving that problem instead - ways of taking a function signature, making specific changes to it (usually adding and/or removing args, but maybe other changes), and then making that your declared signature. At the moment, it's only possible to copy a signature as is (with functools.wraps() and equivalents). ChrisA

On Thu, 28 Jul 2022 at 00:08, Eric V. Smith <eric@trueblade.com> wrote:
Sorry for top posting: I’m on the road.
inspect.signature can help with this.
Technically true, and a good basis for a proposal, but try designing a modified signature and you'll find that it's actually pretty hard. What I'd like to see would be a way to decorate two key types of change: @functools.wraps_ish(func) def wrapper(*a, consumed_arg="spam", **kw): return func(*a, **kw) @functools.wraps_ish(func): def wrapper(*a, **kw): return func(*a, added_arg=1, **kw) with all the variants of keyword and positional args. Being able to say "this function has the same signature as that one, plus it accepts consumed_arg" or "same, but without added_arg" would be extremely useful, and short of some fairly detailed messing around, not easy at the moment. So rather than proposals for weird magic objects that do weird things as function arguments, I'd much rather see proposals to fix the reported signature, which would largely solve the problem without magic. (Or technically, without *more* magic; the only magic needed is "if the function has a __wrapped__ attribute, use the signature from that", which already exists in many tools.) ChrisA

On Wed, Jul 27, 2022 at 1:07 PM Chris Angelico <rosuav@gmail.com> wrote:
I would suggest checking out python-makefun, which can do many transformations on the signature of the method: https://smarie.github.io/python-makefun/ The wrapt library also does some of this, though the process for adding or removing fields is more convoluted than with makefun. It might be interesting to put this in the standard library (e.g. expanding functools.wraps), though there are third party libraries that do a reasonable job.
Note that per PEP 362, tools should also respect the __signature__ magic attribute (and that should override the signature of .__wrapped__ when both are present). Best wishes, Lucas

Mathew Elman writes:
To answer how this _could_ work, Undefined would be a new NoneType
My example is intended to refer specifically to the alternative semantics where 'undefined' is not allowed outside of function prototypes (or other specified contexts, for that matter). The point of the example is that it will be possible to capture that singleton and use it anywhere by a different name. And you just know that somebody will find a reason to do it.
You're right, this should be "global notdefined; notdefined = x".
What does that print? Or does it error?

Yes, I want that when Void is received in a function parameter it is not
Михаил Крупенков: processed:
func(Void) == func() == "default"
Mathew Elman:
I think this can be achieved with a decorator.
(In case formatting is screwed up in the above example, see https://gist.github.com/lucaswiman/995f18763d088555547a74864254ac0b) I would recommend using Ellipsis (...) rather than Void for this purpose so you have a convenient syntax. Best wishes, Lucas

On Mon, 25 Jul 2022 at 19:41, Михаил Крупенков <kno3.2002@gmail.com> wrote:
Yes, I want that when Void is received in a function parameter it is not processed:
func(Void) == func() == "default"
It's very difficult to follow this topic because each of your posts begins a completely new thead, with no reference, no quoted text, nothing. Are you saying that Void is a value that doesn't exist? Or that it isn't a value? Python doesn't have any concept of a "value that isn't a value", and as we've seen from SQL's NULL, there are a lot of really bizarre edge cases when you try to make that work. (NULL is a value, except when it isn't. Honestly, I don't think there's any way to describe it that is accurate, useful, and concise.) Generally, when you're carrying a function signature through *a,**kw, it's best to use functools.wraps() to help out autocomplete tools. Rather than this messy Void type (which isn't a type, and isn't a value, but somehow can be assigned to a variable), perhaps it would be more useful to show where wraps() isn't able to help you, and look into ways to solve that problem? But please, please, fix your mail client first. ChrisA

You can easily achieve this with something like Void = object() def func(param=Void): if param is Void: param = "default" or in the wrapper: Void = object() def wrapper(param=Void, **kwargs): if param is not Void: kwargs["param"] = param return func(**kwargs) With some extra work, you can even create a decorator to handle this automatically. On 25/07/2022 11:39, Михаил Крупенков wrote:

I believe this is a rebirth of a request that has come up many times before, which is to have something like javascript's `undefined` where it means "use the default value" if passed to a function that has a default value or "value not provided" (slightly different to "None").
def foo(x, y=1): ... return x, y
foo(undefined, undefined) undefined, 1
The idea being that wrapper functions don't need to then know what the default is for the function they wrap: def outermost_wrapper(b, c=undefined): return inner_wrapper(5, b, c) def inner_wrapper(a, b=undefined, c=undefined): return func(a, b, c) def func(a=1, b=2, c=3): return a*b*c "undefined" would either have to be an alias for "None" everywhere _except_ function signatures, or be only allowed in function signatures and have special handling. It opens up a can of worms in javascript but has its uses.

Mathew Elman writes:
Either would be a significant complication in Python semantics, though. And I don't really see how the latter works: notdefined = None def foo(x=undefined): # here notdefined = x return wrapped_function(x) # there foo() print(notdefined) To get this "use the default" functionality from #here to #there, either the above has to allow you to capture the object representing undefined but you're not allowed to use the name "undefined" for it (very un-Pythonic), or there has to be some horrifying magic, maybe macro-like, that turns the name "x" into a name that isn't there.
It opens up a can of worms in javascript but has its uses.
If you're phishing, I guess worms are useful. *shudder* Wisecracks aside, I'm not sure I want to read code designed so that 'undefined' is genuinely useful. I'd need to see real code that uses it, plus the translation to Python, plus some argument why it has to be that way, before I'd be willing to go higher than -1 on this complication.

To answer how this _could_ work, Undefined would be a new NoneType that is falsey (just like None) can't be reassigned (just like None) and does everything else just like None _except_ that when it is passed as a function argument, the argument name is bound to the default if it has one instead. I am not sure I understand your example
This would call wrapped_function with "undefined" so it would use the default if present, and print None because notdefined in foo is only in that scope? Did you mean to use a global notdefined in foo? In which case it would print undefined ? In your example there is no default to use... it would be this case: def foo(x=undefined): return bar(x) def bar(x=1): return x print(foo()) # 1 print(foo(2)) # 2 To be clear, I am _not_ +anything for this, I was just saying that I think this is what is really be asked for here. I am -0, I can see there being value, but that value is largely superficial and the possible can of worms is definitely a writhing one. The only problem it solves is allowing wrappers to fallback to their wrapped functions defaults, but at a fairly high cost.

On Wed, 27 Jul 2022 at 21:13, Mathew Elman <mathew.elman@ocado.com> wrote:
To answer how this _could_ work, Undefined would be a new NoneType that is falsey (just like None) can't be reassigned (just like None) and does everything else just like None _except_ that when it is passed as a function argument, the argument name is bound to the default if it has one instead.
Okay. Everything else just like None. So I can do this? a = Undefined b = {1: Undefined} c = {Undefined: 2} print(b[1]) a in c # s/be True c[a] # s/be 2 What would these do? And especially, what would happen if, instead of vanilla dictionaries, b and c were custom classes? ChrisA

I don't see why you couldn't. I guess what they do depends if any of these have defaults? Which I think they do not in this case, right? If they were non vanilla dictionaries that had a default e.g. class SomeDict(dict): def __getitem__(self, item=None): return super().__getitem__(item) def __contains__(self, item=None): return super().__contains__(item) print(b[1]) would still print Undefined because print's first argument doesn't have a default. a in c would be False. c[a] would return c[None], which would raise an error here because None isn't in the mapping Again, I am not pro this idea, just answering the questions you're asking as I see them :)

On Wed, 27 Jul 2022 at 21:54, Mathew Elman <mathew.elman@ocado.com> wrote:
So Undefined would trigger a default, but only if there is one?
print(b[1]) would still print Undefined because print's first argument doesn't have a default.
But print doesn't have a first argument - it accepts *args. So Undefined still counts as an argument, except when there's a default, at which point it counts as a non-argument?
a in c would be False.
That is VERY surprising behaviour. For literally any other object, that would be True. But then, it would be surprising in other ways if Undefined behaved like a normal object.
c[a] would return c[None], which would raise an error here because None isn't in the mapping
Again, very surprising, if putting a value into a mapping doesn't result in that value being in the mapping.
Again, I am not pro this idea, just answering the questions you're asking as I see them :)
Yeah. I think you're doing a great job of showing why this is a bad idea :) ChrisA

Chris Angelico wrote:
Yes, I guess you could do it so that for var args it was treated as a non argument? so print(Undefined) == print() ? But that seems even more surprising...
Yes, compared with other objects, there would need to be some surprising behaviour because it isn't a concept in python already. You could fix this specific problem with mappings by changing Undefined to not be hashable, but that is definitely a bandaid.
Again, I am not pro this idea, just answering the questions you're asking as I see them :) Yeah. I think you're doing a great job of showing why this is a bad idea :)
I do think the desire to fix the "wrapper not needing to know the defaults of wrapped" problem is admirable. Is this the right approach? No, I think not.

On Wed, 27 Jul 2022 at 22:54, Mathew Elman <mathew.elman@ocado.com> wrote:
Wrapper not needing to know the defaults of the wrapped can be solved with *a,**kw. This modifies the problem into "callers of the wrapper now don't know anything about the signature". I'd rather look into ways of solving that problem instead - ways of taking a function signature, making specific changes to it (usually adding and/or removing args, but maybe other changes), and then making that your declared signature. At the moment, it's only possible to copy a signature as is (with functools.wraps() and equivalents). ChrisA

On Thu, 28 Jul 2022 at 00:08, Eric V. Smith <eric@trueblade.com> wrote:
Sorry for top posting: I’m on the road.
inspect.signature can help with this.
Technically true, and a good basis for a proposal, but try designing a modified signature and you'll find that it's actually pretty hard. What I'd like to see would be a way to decorate two key types of change: @functools.wraps_ish(func) def wrapper(*a, consumed_arg="spam", **kw): return func(*a, **kw) @functools.wraps_ish(func): def wrapper(*a, **kw): return func(*a, added_arg=1, **kw) with all the variants of keyword and positional args. Being able to say "this function has the same signature as that one, plus it accepts consumed_arg" or "same, but without added_arg" would be extremely useful, and short of some fairly detailed messing around, not easy at the moment. So rather than proposals for weird magic objects that do weird things as function arguments, I'd much rather see proposals to fix the reported signature, which would largely solve the problem without magic. (Or technically, without *more* magic; the only magic needed is "if the function has a __wrapped__ attribute, use the signature from that", which already exists in many tools.) ChrisA

On Wed, Jul 27, 2022 at 1:07 PM Chris Angelico <rosuav@gmail.com> wrote:
I would suggest checking out python-makefun, which can do many transformations on the signature of the method: https://smarie.github.io/python-makefun/ The wrapt library also does some of this, though the process for adding or removing fields is more convoluted than with makefun. It might be interesting to put this in the standard library (e.g. expanding functools.wraps), though there are third party libraries that do a reasonable job.
Note that per PEP 362, tools should also respect the __signature__ magic attribute (and that should override the signature of .__wrapped__ when both are present). Best wishes, Lucas

Mathew Elman writes:
To answer how this _could_ work, Undefined would be a new NoneType
My example is intended to refer specifically to the alternative semantics where 'undefined' is not allowed outside of function prototypes (or other specified contexts, for that matter). The point of the example is that it will be possible to capture that singleton and use it anywhere by a different name. And you just know that somebody will find a reason to do it.
You're right, this should be "global notdefined; notdefined = x".
What does that print? Or does it error?

Yes, I want that when Void is received in a function parameter it is not
Михаил Крупенков: processed:
func(Void) == func() == "default"
Mathew Elman:
I think this can be achieved with a decorator.
(In case formatting is screwed up in the above example, see https://gist.github.com/lucaswiman/995f18763d088555547a74864254ac0b) I would recommend using Ellipsis (...) rather than Void for this purpose so you have a convenient syntax. Best wishes, Lucas
participants (9)
-
Anthony Flury
-
Chris Angelico
-
Eric V. Smith
-
Greg Ewing
-
João Santos
-
Lucas Wiman
-
Mathew Elman
-
Stephen J. Turnbull
-
Михаил Крупенков