Skip to content
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

VIP: Allow usage of min with a literal to set iterators #1047

Closed
ElianaTroper opened this issue Oct 13, 2018 · 10 comments
Closed

VIP: Allow usage of min with a literal to set iterators #1047

ElianaTroper opened this issue Oct 13, 2018 · 10 comments
Labels
VIP: Discussion Used to denote VIPs and more complex issues that are waiting discussion in a meeting
Milestone

Comments

@ElianaTroper
Copy link
Contributor

ElianaTroper commented Oct 13, 2018

Simple Summary

Allow usage of min with a literal to set iterators e.g. min(22, len(someBytes))

Abstract

Currently, iterators must be set with a literal. A minimum which contains a literal reasonably should be no larger than said literal, but could theoretically be smaller based upon the other value.

Motivation

This could reduce the number of loops used (and any calls contained within) in situations where the minimum number of loops may fall below a set value e.g. looping over a byte array of max length 64, but it may be shorter.

Specification

I would expect a contract similar to the following to compile

@private
def __main__(input: bytes[64]) -> int128:
    _x: int128 = 0
    _len: int128 = len(input)
    for i in range(min(64, _len)):
        _x = i
    return _x

Backwards Compatibility

Yes, doesn't change any previously compatible literal inputs for range

Copyright

Copyright and related rights waived via CC0

@jacqueswww
Copy link
Contributor

jacqueswww commented Oct 14, 2018

Looks like a reasonable change to me. The only problem is that if one of the parameters isn't a reasonable runtime value it could cause trouble.

@fubuloubu
Copy link
Member

fubuloubu commented Oct 14, 2018

Well, I think for var in ls: ... covers the majority of the use cases you might have here.

The one modification is that since len(ls) is actually always a constant (since we disallow dynamic arrays), we could allow this syntax in place of having to write the numbers everywhere.

But again, since we have named constants now, you could just use that everywhere too e.g.

N: constant(uint8) = 64

@private
def __main__(input: bytes[N]) -> int128:
    _x: int128 = 0
    _len: int128 = len(input)
    for i in range(N):
        _x = i
    return _x

We have a call tomorrow, let's discuss!

@jacqueswww jacqueswww added the VIP: Discussion Used to denote VIPs and more complex issues that are waiting discussion in a meeting label Oct 15, 2018
@jacqueswww
Copy link
Contributor

jacqueswww commented Oct 16, 2018

We discussed this issue at our meeting yesterday. We came to the conclusion that we would need more details from the specific use case that is required, however whitelisting range(min()) isn't safe. but we did come with two sensible use cases:

1.) iterating per byte until len of a bytearray.

input: bytes[100] = "test data"
for idx, b in enumerate(input):

2.) whitelisting a statement of for i in len(bytes[x]) which would iteration of the len value.

input: bytes[100] = "test data"
for i in enumerate(input):

@SamuelTroper What are your thoughts? Does 1 or 2 cover the use case?

@fubuloubu
Copy link
Member

I'd like to note that bytes[100] is a "number of bytes less than or equal to 100". Therefore both iterator and len() should return 9, the length of the actual data (I think)

@ElianaTroper
Copy link
Contributor Author

ElianaTroper commented Oct 16, 2018

@jacqueswww assuming 2 is supposed to be

input: bytes[100] = "test data"
for i in range(len(input)):

I think that would solve the use case I was thinking of. Additionally, if that were implemented you could effectively get the result of 1 just by slicing the input to one byte each loop.

Also, for clarity enumerate is not currently implemented, correct?

Use case I was envisioning was storing multiple different types (with very different sizes) of data in the same byte array with some bytes used to identify what type of data was being stored.

@fubuloubu
Copy link
Member

I think 2 is actually for i in range(len(input)) which should output 0..8 if input is of length 9.

Your use case sounds sort of like RLP decoding. We have a macro for that.

@ElianaTroper
Copy link
Contributor Author

@fubuloubu I hadn't looked into RLP much before but man that's a nice standard. Thanks for pointing that out.

@fubuloubu
Copy link
Member

The following works:

@public 
def test(input: bytes32[64]): 
    for b in input: 
        ...

But you cannot iterate over a bytes object itself:

@public 
def test(input: bytes[64]): 
    for b in input: 
        ...
StructureException: line 4:13 Cannot iterate over 'Name' object
     3 def test(input: bytes[64]):
---> 4     for b in input:
--------------------^
     5         pass

The solution for this would be implement the bytes1 type, similar to #967 and #1616

@charles-cooper
Copy link
Member

due to offline feedback, probably will deprecate this proposal in favor of #2527

@charles-cooper
Copy link
Member

Not planned since #3537 implements #2527 .

@charles-cooper charles-cooper closed this as not planned Won't fix, can't repro, duplicate, stale Jul 25, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
VIP: Discussion Used to denote VIPs and more complex issues that are waiting discussion in a meeting
Projects
None yet
Development

No branches or pull requests

4 participants