-
-
Notifications
You must be signed in to change notification settings - Fork 837
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow default parameters #903
Comments
def overloaded_function(a: uint256, b: bytes32=""):
... Creates I like it! Very pythonic solution to the problem! |
Can also specify non-zero default args |
Yes, at first glance I like it too. :) This is not my final opinion, let me mull this over a bit some more (weekend brain). |
Marked as Approved. |
I think this is not accurate if we assume keyword arguments are in play. It will be def overloaded_fn(a: uint256=3, b: uint256=4):
... This would be equivalent to the following three functions def overloaded_fn(1): # a=1, b=4
def overloaded_fn(b=1): # a=3, b=1
def overloaded_fn(): # a=3, b=4
... |
|
Restrict to a maximum of 3 default arguments? (6 generated function signatures) |
My suggestion which I think was generally agreed upon in chat
|
Out of curiosity, is there any sense of the cost in terms of runtime bytecode for each new selector? Is it only the code required to dispatch and JUMP to the corresponding code segment, or will the bytecode for the function body also need to be duplicated? |
The way we were discussing it, it would be a dispatch statement. So basically, load the stack with the call args and/or defaults, then jump to the code. The problem is the exponential growth (7 default args produces 28 different calling loaders). At some point it becomes untenable. |
7 default args is 2^7 = 128 loaders if you allow arbitrary order. If you only allow the right-most arguments to be omitted then that is 7+1 = 8 loaders. Both solutions are probably fine because it is the programmer's damn fault if they are designing an external function that way! Bytecode for the function body does not need to be duplicated. Also if you are only allowing right-most arguments to be omitted then this allows you to implement in an iterative fashion: callFunction(1,2,3, defaultA, defaultB) encodes like this: ... callFunction_with_2_defaults: callFunction_with_1_default: callFunction: |
(7 * (7 + 1)) / 2 = 28 is the right number, because order is important but you don't have to necessarily require right to left ordering of default value settings (e.g. we don't need to disallow Regardless, jumping to each signature and loading the corresponding default is a good implemetation suggestion if we require right to left ordering of defaults. I would suggest this be the final implemetation when we have figured out the whole internal call thing. EDIT: I think you're right, N + 1 is the real answer to the growth of the signature stack. So it's linear, not exponential. We probably don't even need a special case for this, I don't think anyone would legitimately use more than 7 default args |
Starting on this. |
Sure you can! Anonymous default parameters can only be the rightmost parameters. So fun(int a, int b=5, int c=6) is unambiguous when you call fun(7, 2). |
so, by that logic: |
fun(7,2) calls Three external entry points total. |
So EDIT: This is where I got the fall-through idea from @jacqueswww aka this is the reason why the number of function selectors is |
Ah. For internal it does not matter. There is no function selector for internal calls. |
@fulldecent there still needs to be some unique identifier to jump to though ;) (and until #901 gets implemented still needs the method_id) So I have implemented the necessary changes, and it's looking good, at this stage it still uses the If everyone one is OK with moving to positional variations (n+1) only. I will make the changes. Basically:
fun(int128) # a = ?, b = 1, c = 2
fun(int128, int128) # a = ?, b = ?, c = 2
fun(int128, int128, int128) # a = ?, b = ?, c = ? Exposed on the ABI. |
The unique identifier is the JUMPDST instruction byte offset! But yes, I fully agree with your proposal here. |
Yes but in the IR, you'd still generate a unique label/symbol that's all I meant ;) |
This is a reduced scope feature as compared to adding Function Overloading #884.
Introduction
ERC-721 includes two functions with the same name but one includes an extra parameter. The ERC specification states that the less parameter one will function equivalently as if the more parameter one were called with a specific value.
Specification
Allow default parameters at function declaration.
Here is a very relevant example. Includes NatSpec documentation as per #898.
Sorry, not sure if my bytes declaration is correct.
The Vyper compiler will process this as two separate function selectors. Actually (1 + number of default parameters) function selectors. This follows the Python convention of allowing parameters to be omitted if a default parameter is specified. This differs from Python in that Python function arguments can be specified out of order.
The Vyper compiler can optimize byte code more efficiently than Solidity currently does because Vyper will have more knowledge about the relationship between
safeTransferFrom(a,b,c,d)
andsafeTransferFrom(a,b,c)
.Literature review
Vyper has expressed opinions on function overloading in general:
Strictly speaking, a default parameter is a function overload. From the EWM there are two function descriptors, but from Vyper, there is one function. I believe the opinions above apply to allowing arbitrary function overloads, whereas this issue is only a very specific kind of overload.
With this proposed feature, we centralize implementation for both code paths in one function. This solves the issue of tracking which call refers to which function, they are the same function. Also this implementation is much safer than Solidity. In Solidity, as this can cause confusion. The foo() logs but foo(param) steals funds example above does not apply because BOTH are the same function.
Discussion
Honestly I don't feel like this is a compromise. I believe this actively promotes Vyper's goal to improve human-readability of code and prevent misleading code.
Pinging people from function overloading discussion: @maurelian @fubuloubu @jacqueswww @ben-kaufman
The text was updated successfully, but these errors were encountered: