Introduce a real constant keyword and rename the current behaviour
axic opened this issue ยท 47 comments
I think the following makes more sense compared to what we have now.
Now:
constant
function should not modify the state (not fully enforced yet)constant
state variable (ie. the one in the class and not in a method) is evaluated every time it is called
After the change:
-
- the keyword
view
is introduced for functions (it replacesconstant
). Calling aview
cannot alter the behaviour of future interactions with any contract. This means such functions cannot useSSTORE
, cannot send or receive ether and can only call otherview
orpure
functions.
- the keyword
-
- the keyword
pure
is introduced for functions, they areview
functions with the additional restriction that their value only depends on the function arguments. This means they cannot useSSTORE
,SLOAD
, cannot send or receive ether, cannot usemsg
orblock
and can only call otherpure
functions.
- the keyword
-
- the keyword
constant
is invalid on functions
- the keyword
-
- the keyword
constant
on any variable means it cannot be modified (and could be placed into memory or bytecode by the optimiser)
- the keyword
5) the keyword[agreed to remove this functionality]volatile
is introduced for variables, which read their referenced value every time (this is whatconstant
does today to variables)
Any example of the volatile
keyword:
contract A {
uint volatile number = block.number;
function a() returns (uint) {
return number; // this returns the *current* block.number every time it is called
}
}
I'm not sure the volatile
option is useful and thus we might decide to remove it.
the keyword stateless is introduced for functions (it replaces constant)
This seems like the wrong word; constant
functions are definitely not stateless; they often depend on state. If a new name must be used, perhaps something like immutable
or pure
(in the sense that a pure function is side-effect-free, which is what we're really describing)?
I'm trying to find the word and stateless ia far from perfect. It should describe that the state is not modified.
Also additionally we could introduce real constant functions, which are indeed stateless and dont depend on the state. I.e. calculating something based on the inputs.
Well, from a CS-theoretic POV, 'pure' is definitely the term you want. More informally, 'side effect free', but that's harder to condense into a keyword.
I think 'immutable' will be the least ambiguous to most developers.
I like pure, but are we likely to find another use for it? Does view as keyword make sense?
@redsquirrel immutable wouldmean to me that child classes cannot ovveride it.
@axic I suggest following Java and using 'final' for functions that cannot be overridden.
I like pure, but are we likely to find another use for it?
I don't think so; certainly this is the only usage for the term I'm aware of.
I suggest following Java and using 'final' for functions that cannot be overridden.
I don't think that was under discussion here?
I agree that we should use pure...perhaps strict
? But I really like the idea of pure
as a side effect free input. Rather than volatile
which conjures up bad imagery, why not static
?
nonmutating
?
too long...overly blunt descriptive. Less is more imo.
readonly
?
what's wrong with pure
or strict
as is used in js?
Many of the definitions of pure
say it does not depend on state and no matter when it is called, based on the input parameters it should always return the same predictable result.
Therefore it does not suit our application.
@VoR0220 static
does not reflect what it is. The volatile
part is a different behaviour (= what constant variables do).
Updated the description with further clarification which should help coming up with a good name.
hmmm...so I like that idea in regards to pure
it enables solidity to become "functional" (and pleases all the zealots), but in regards to view
...I think it might be better to just bite the bullet and call it immutable
(because that's what it is). As for volatile
, there are uses for it, but not many...probably just call it mutable
.
The problem with immutable
is that you cannot apply that adjective to the function. The function itself never changes.
right but the output does. Perhaps you could create such a differentiation. You might do something like this
function f) returns immutable (my, output, signature) {
}
@VoR0220 it is not the output or the function which is mutable/immutable.
Case 1: function can read state variables (sload). The output of this function will depend on the current state.
Case 2: function can modify state variables (sstore). The output of this function will depend on the current state, as well as it can change the state, which then affects any future invocations.
I've borrowed view
from databases. The function provides a modifiedview
of the current state (i.e. it uses sload and applies transformations).
Can we agree that the behaviour called volatile
above should not be present and we can remove it?
I don't see much need for volatile
.
Many of the definitions of pure say it does not depend on state and no matter when it is called, based on the input parameters it should always return the same predictable result.
Therefore it does not suit our application.
Doesn't the current proposal fit pure
? 2) the keyword pure
is introduced for functions (they cannot call SSTORE or SLOAD)
Which part is pure
breaking?
I think the 4 proposals are good. No one else has made comments about readonly
, and I think it fits well with in the 1st proposal, which would read as:
- the keyword
readonly
is introduced for functions (it replaces constant, these functions cannot call SSTORE, but can call SLOAD)
readonly
and pure
sound accurate enough.
Doesn't the current proposal fit
pure
?
Sorry for the confusion, the description was updated after those comments. pure
(and every other keyword) was initially proposed for what 1) is. It fits 2) well.
For 1) I like view
, readonly
, and nonmutating
(but this is way too long).
Many of the definitions of pure say it does not depend on state and no matter when it is called, based on the input parameters it should always return the same predictable result.
Yes, you're right - I stand corrected. readonly
is probably the sanest I've seen so far, though view
also works.
RE constant/volatile/etc, besides dropping the functionality (which seems good to me), why not call it 'alias' or 'define'? That seems to be effectively what it is.
I think that volatile
is just a buggy behaviour of constant
. We can introduce something like alias
in the future, it is certainly planned for types (using x = y
) and could also be introduced for general expressions.
I updated the description to specify the behaviour in more detail.
Question: Can a pure function return address(this)
? I.e. is it required to be pure just from its source code or is it fine to be pure "after deployment"? Can a pure function return a storage value that it set in the constructor (i.e. from the block hash) and never modified in the contract?
From the keywords: The problem with readonly
is similar to the one with immutable
- it applies to the function while it should apply to the state. On the other hand, view
is not an adjective.
@chriseth moved alias
into its own issue and removed volatile
from the description.
I.e. is it required to be pure just from its source code or is it fine to be pure "after deployment"?
That is a good question. I would argue that it is more useful to allow this
and any constants set during the constructor (if that becomes possible).
Storage should not be accessed by pure
functions unless we introduce writeonce
storage slots set during the constructor, but what is the reason to use them? They are more expensive than storing it in bytecode.
Further discussion: Are events allowed in view functions? I would rather say no, as they have an effect on the blockchain even if that effect is not visible from within the EVM.
View functions should not be able to spend any gas.
@ethernomad if they are executed on chain, they will always spend gas, but they can be used together with the new static_call
opcode that is under discussion and they will be used together with eth_call
if invoked from web3.js.
Are events allowed in
view
functions?
I think this question also applies to pure
. It is a though question, logs do change the state, but in a way which doesn't influence the future execution of either view
or pure
functions.
Let us write a specification about what is and what is not allowed (into the description of this issue). Things to be sorted: be sorted: selfdestruct, log (events), this.balance, address.balance, tx.*, new
Why do we need to sort those ones out? Can I haz links?
For volatile
, would you be able to do this:
uint volatile foo = block.number + block.timestamp;
Or even:
uint volatile foo = this.bar()
If so, speaking from my experience as a python developer who has dealt many times with debugging code involving @Property declarations, this sounds extremely dangerous. Things should do what they seem to do and nothing else; variable lookups that are actually function calls that might lead to re-entrant weirdness sound like a nightmare for auditing and could potentially mislead contract readers.
volatile
describes what constant
does currently. We have agreed to remove this functionality and will perhaps introduce typedef / aliases later: #1013.
Note that this "volatile" thing is abusing one application of the constant
keyword. This application was intended to provide actual compile-time (or at least deploy-time) constants. As everywhere else, this is not yet correctly enforced.
Things to be sorted: be sorted: selfdestruct, log (events), this.balance, address.balance, tx.*, new
Perhaps we need to define for view
whether it can take the state of the contract only or the entire state as an input.
pure
:
- cannot use
selfdestruct
,new
,log
,this.balance
,address.balance
,tx.*
,msg.*
,block.*
view
:
- cannot use
selfdestruct
,new
- can use
this.balance
- can use
tx.*
,msg.*
,block.*
Canonically speaking, view
should not use log
as it changes the state, however, currently it changes the state in a way it won't affect future invocations of view
. Being future proof, I would say it shouldn't be allowed to use it.
You are right, in serenity, log will be changed to actually make modifications to storage of a certain contract. This means, log should be viewed as a call to a non-view, non-pure function of another contract. Because of that, view should not be able to use log, I would say.
so events are no longer going to be free/near free in terms of gas?
I think we more or less found that our framework is not yet fully specified because pure functions might modify memory. My current proposal would be the following:
view and pure are attributes applied to the function, and they only concern storage and other permanent state.
The pure keyword should be renamed, because a "pure" function is still able to modify memory references that are passed in (but not storage references). In order to prevent this, the type of such an input variable has to be <memory type> constant
.
So in essence, "view" and "pure" apply to "this" (and other state including other contracts) and all other input variables have their own specifiers.
I suggest the new name as expression
or volatile
that will evaluate in each call.
See this code:
contract LockableCoin is AbstractCoin {
//creation time, defined when contract is created
uint public creationTime = now;
//time constants, defines epoch size and periods
uint public constant UNLOCKED_TIME = 25 days;
uint public constant LOCKED_TIME = 5 days;
uint public constant EPOCH_LENGTH = UNLOCKED_TIME + LOCKED_TIME;
//current epoch constant formula, recalculated in any contract call
uint public constant CURRENT_EPOCH = (now - creationTime) / EPOCH_LENGTH + 1;
//next lock constant formula, recalculated in any contract call
uint public constant NEXT_LOCK = (creationTime + CURRENT_EPOCH * UNLOCKED_TIME) + (CURRENT_EPOCH - 1) * LOCKED_TIME;
//(...)
}
From https://github.com/ethereans/github-token/blob/master/contracts/LockableCoin.sol
Why word volatile being not considered? I see use cases for this, and its really useful and easy to understand. Volatile are expressions that result a value change due contract storage, chain or call variables change.
Such as:
//caller balance
mapping (address => uint) balances;
uint volatile callerBalance = balances[msg.sender];
//lock epoch
uint256 public creationTime = now;
uint256 public unlockedTime = 25 days
uint256 public lockedTime = 5 days;
uint256 public volatile EPOCH_LENGTH = unlockedTime + lockedTime;
uint256 public volatile CURRENT_EPOCH = (now - creationTime) / EPOCH_LENGTH + 1;
uint256 public volatile NEXT_LOCK = (creationTime + CURRENT_EPOCH * unlockedTime) + (CURRENT_EPOCH - 1) * lockedTime;
I was first fooled by a constant owner that changed in any call, but using a special keyword for this case makes sense.
Just removing this hability makes solidity more restrictive.
Volatile is useful when you need a constant that is synced to various variables and is used a lot of times in almost all functions, if not this case, internal function and a memory var is better.
I like to restrict the number of keywords as much as possible, especially if there is another solution that does almost the same thing.
Here is a summary of the current design:
There's enum for statemutability
, from most restricted to least restricted: pure
, view
, nonpayable
, payable
. A function can only be one of these.
view:
- cannot use
selfdestruct
,new
- can use
this.balance
- can use
tx.*
,msg.*
,block.*
- can only call
view
orpure
functions - cannot send ether
- can use
sload
- cannot use
sstore
pure:
- cannot use
selfdestruct
,new
,log
,this.balance
,address.balance
,tx.*
,msg.*
,block.*
- can only call
pure
functions - cannot use
sload
- cannot use
sstore
Remarks:
- JSON ABI has a new field
statemutability
introduced with a string value as above - JSON ABI keeps
constant
/payable
for backwards compatibility for a while - constructor / fallback cannot be
view
orpure
constant
as a specifier on functions is phased out (removed at the next breaking release)
This is finally implemented and documented. To only thing left is enforcing these restrictions and that is tracked in #2606.
What version is this implemented in?
@fulldecent I believe view
and constant
are both working right now, I would like to see readonly
, but for compatibility I prefer to stick with view
, it's still less ambiguous than constant
anyway
https://ethereum.stackexchange.com/questions/25200/solidity-what-is-the-difference-between-view-and-constant
Is it normal behavior for a pure
function to access a constant
"state variable"?
It make sense architecture-wise, but is this intended and will be doubled down on?
A constant
"state variable" is just a constant value, it is not a storage variable.