bank: public({
account_balance: num,
person: bytes32
}[num])
@public
@payable
def transfer(sender_accno: num, reciever_accno: num, amount: num):
if self.bank[sender_accno].account_balance >= amount:
pass
throwing error "AttributeError: 'Name' object has no attribute 'value'
" at this line
if self.bank[sender_accno].account_balance >= amount:
account_balance and amount are both of same type 'num' and it does support >= comparison operator. So, what's wrong with that?
Hi! I was using 'dynamic' in a very fast and loose sense based on the issues I read and vyper's surface similarity to python. But after reading more of the vyper source and testing it out, I found that vyper is much more static than I originally thought.
For instance, based on the following issue ethereum/vyper#582, it looked like [ 1, 2.0, 3 ]
was valid vyper. (allowing mixed type lists would result in a dynamically typed language because you can't determine the list element types at compile time). When i went to test it out though, and reading through this channel's history, it looks like mixed typed lists don't compile.
Another thing I noticed was about the semantics of None
. It seems that None
inhabits every type - but at least you can't have a None
with underspecified type (e.g. foo = None
is an invalid declaration of foo
). Reading through some IR output though it seems None
is just an alias for 0
, which seems typesafe but obfuscating.
My comment about dynamic execution was based on ethereum/vyper#590, as well as vyper's surface similarity to python (which allows code rewriting at runtime). But I see after playing with vyper some more that block-local functions (or really any other mechanism which could result in contract-local looping/recursion) are disallowed. This is good because it improves the ability to statically trace execution! Now correct me if I'm wrong here but does this mean vyper is not Turing complete? Or is there still a back door to recursion through remote calls via raw_call
?
So with the caveat that control flow is a very tricky design area, I think static analysis would be improved further though by requiring all if statements to have both branches and disallowing short-circuiting / remote calls except in very explicit contexts. For instance in Haskell, 'short-circuiting' basically requires a monadic context in order to happen. I think having a limited effects system (which decorators already provide some mileage towards) could help solve a lot of these problems.
For instance, I was thinking about ethereum/vyper#590 and it seems that the issue is stemming from having two separate 'effect systems' for functions-which-don't-return-anything and functions-which-return-something. If they had been reified into a single type, making pass
an alias for return void
(and disallowing signatures like def foo()
, instead requiring def foo() -> void
), then of course def foo() -> num
couldn't end in pass
(aka return void
) because that wouldn't type check.
It's just an idea - in practice it would probably be too draconian to require users to add return void
everywhere, although perhaps functions could be annotated with default return values.
if
and requiring void
return type could be solved by implicit assumption in an intuitive and safe way
else pass
implicitly is okay from a static analysis perspective
return [void]
(you can call return without any type to output) when no return exists in the function is also pretty okay
@fubuloubu how do you feel about forcing each branch of an if statement to have the same effect type? Take for instance the following example
def foo (x: num) :
if x < 2 :
x = 2 # modifies locally scoped variable, call it the 'locally scoped mutation effect'
elif x < 10 :
self.balances[self.owner] = x # modifies contract state, call it the 'contract mutation effect'
elif x < 15 :
return 1 # exits the function, call it the 'control flow effect'
else :
raw_call(stuff) # calls to external address, call it the 'external call effect'
return 0
Each branch has fundamentally different properties which you might want to segregate. If you forced each branch to have the same kind of effect, it might be easier to perform certain kinds of analysis on these blocks.
decimal
in its constructor, but I can't work out how the heck to pass a literal using web3.py
. I've unsuccessfully tried literals of the form: 0.2
, 0
, "0"
, "0x0"
, 1/2
... Is this not yet possible? (I found this: ethereum/EIPs#598)
Decimal
type, with no luck)