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

feat: add algopy.Array and algopy.ImmutableArray #386

Open
wants to merge 91 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
2a7ae47
feat: add scratch based Array support
daniel-makerx Jan 29, 2025
4bb7caa
chore: compile all
daniel-makerx Jan 30, 2025
2d399ac
refactor
achidlow Feb 3, 2025
732ce99
Dan made me write this
achidlow Feb 3, 2025
799fc61
add ArrayEncode
daniel-makerx Feb 3, 2025
c6e77e5
remove ArrayExtend
daniel-makerx Feb 3, 2025
59909e0
chore: compile all
daniel-makerx Feb 3, 2025
76262bf
remove unused code
achidlow Feb 3, 2025
bba600d
feat: add `algopy.ImmutableArray`
daniel-makerx Jan 30, 2025
28590f5
fix: fix encoding of an array of arc4 bools when extending from a tup…
daniel-makerx Feb 4, 2025
5b60abd
rename some functions
daniel-makerx Feb 4, 2025
71c4c69
add expected output test for supported array elements
daniel-makerx Feb 4, 2025
add5cdf
chore: compile all
daniel-makerx Feb 4, 2025
2876818
chore: ignore trace files
daniel-makerx Feb 4, 2025
74e37d5
tests: add immutable array routing tests
daniel-makerx Feb 4, 2025
2f94a16
refactor: add wtype_to_arc4_wtype typing overloads
daniel-makerx Feb 4, 2025
f1f248d
fix immutable array iteration
daniel-makerx Feb 4, 2025
f6b351f
chore: compile all
daniel-makerx Feb 4, 2025
d5fb016
nested immutable array support
daniel-makerx Feb 4, 2025
72329fb
chore: compile all
daniel-makerx Feb 4, 2025
16b221c
remove debug code
daniel-makerx Feb 4, 2025
7491e49
add additional test case for order of evaluation in an assignment sta…
daniel-makerx Feb 6, 2025
0f4d160
simplify
daniel-makerx Feb 6, 2025
ce6239c
add notes about the ordering of evaluation of assignment nodes
daniel-makerx Feb 6, 2025
fef49e8
docs: Array docs (WIP)
daniel-makerx Feb 5, 2025
d51089f
restrict chained extract optimisation to known safe cases && reduce o…
achidlow Feb 6, 2025
a95a186
chore: compile all
achidlow Feb 6, 2025
4a734a4
refactor + make more conservative for now
achidlow Feb 6, 2025
27c2eea
simplify
achidlow Feb 6, 2025
c36f8c8
reduce diff
achidlow Feb 6, 2025
e6503e9
correctly describe immutable array types in ARC-56
daniel-makerx Feb 6, 2025
5bfb70a
chore: compile all (rebase)
daniel-makerx Feb 6, 2025
0209c75
reduce diff
achidlow Feb 6, 2025
1f548ba
minor change
achidlow Feb 6, 2025
aaf6be2
use the term static size instead of fixed size
daniel-makerx Feb 7, 2025
e4fb688
docs: add data structures intro
daniel-makerx Feb 7, 2025
401e3ed
chore: compile all
daniel-makerx Feb 7, 2025
20d44f5
move ArrayReplace node validation to AWST
achidlow Feb 7, 2025
6dfc87a
Apply suggestions from code review
daniel-makerx Feb 7, 2025
d4edc02
minor fix
achidlow Feb 7, 2025
ef7cc11
minor simplification
achidlow Feb 7, 2025
5176454
hmmmmming intensifies
achidlow Feb 7, 2025
9f89399
docs: explain how array conversions are done
daniel-makerx Feb 7, 2025
4f01122
refactor: provide source location where possible for wtype_to_ir_type
daniel-makerx Feb 7, 2025
03f8e3d
refactor: add source location to effective_array_encoding
daniel-makerx Feb 7, 2025
7a6ed4c
add more validation for array element types
daniel-makerx Feb 7, 2025
283090b
minor refactor
achidlow Feb 7, 2025
a50f7f8
isolate the presumptive encoding of immutable-arrays to IR layer
achidlow Feb 10, 2025
5b65e6f
redundant code elimination
achidlow Feb 10, 2025
05d6e4f
linting / expand ternary
achidlow Feb 10, 2025
d0123d0
- add missing `__add__` implementation to ImmutableArray expression b…
achidlow Feb 10, 2025
a8fba9a
saving some for Dan
achidlow Feb 10, 2025
824f0be
add tests for array tuple extends
daniel-makerx Feb 10, 2025
72269c2
remove TODO
daniel-makerx Feb 10, 2025
4cc7348
chore: compile all
achidlow Feb 10, 2025
52facd5
wip 🤕
achidlow Feb 10, 2025
14ddf76
only decode array item if required
tristanmenzel Feb 10, 2025
276998a
wip 🤕
achidlow Feb 11, 2025
13919bc
allow disabling specific optimizations (internal only)
daniel-makerx Feb 11, 2025
1b9d418
ensure array coverage at O0
daniel-makerx Feb 11, 2025
60b2131
add assert for popped values
daniel-makerx Feb 11, 2025
750bc98
chore: compile all
daniel-makerx Feb 11, 2025
8699a11
fix bug with incorrect indices when popping from array
daniel-makerx Feb 11, 2025
dbd4665
chore: compile all
daniel-makerx Feb 11, 2025
bcb7847
- split ref and stack arrays as types, not just one immutable property
achidlow Feb 11, 2025
085c693
comment
achidlow Feb 11, 2025
b9e69a1
wip done?
achidlow Feb 11, 2025
06f0a65
minor changes
achidlow Feb 11, 2025
cc22915
test concat immutable array
daniel-makerx Feb 11, 2025
aa6f61e
test index native array of arc4 elements
daniel-makerx Feb 11, 2025
de6a8f4
remove todo
daniel-makerx Feb 11, 2025
c5a799a
chore: compile all
daniel-makerx Feb 11, 2025
1e57aad
increase AWST validation
achidlow Feb 11, 2025
268c591
increase AWST validation
achidlow Feb 11, 2025
9a55383
simplify types in validators, since we don't have to worry about catt…
achidlow Feb 11, 2025
b1255da
extract function from the beastie boi
achidlow Feb 11, 2025
22a0895
wip 🐍
achidlow Feb 11, 2025
f8e32b5
wip 🐍
achidlow Feb 11, 2025
bcc357b
wip 🐍
achidlow Feb 11, 2025
03f68e9
wip 🐍
achidlow Feb 11, 2025
189345c
wip 🐍
achidlow Feb 11, 2025
2fa13f1
wip 🐍
achidlow Feb 11, 2025
599161d
remove unused code
achidlow Feb 11, 2025
9767684
remove rake from iteration garden
achidlow Feb 11, 2025
bfe2c39
grammarify
achidlow Feb 11, 2025
510c278
increase AWST validation
achidlow Feb 11, 2025
ca4b467
fix for the fix for the fix
achidlow Feb 11, 2025
da27a81
fix for the fix for the fix
achidlow Feb 11, 2025
14cf9f2
fix imports
achidlow Feb 11, 2025
ede229e
this is now a load bearing assumption
achidlow Feb 11, 2025
6d3d3e1
my way or the highway
achidlow Feb 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
The diff you're trying to view is too large. We only load the first 3000 changed files.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ examples/**/*.trace
# coverage output
.coverage
coverage.xml
# simulate trace files
*.trace.avm.json
1 change: 1 addition & 0 deletions docs/language-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ lg-control
lg-modules
lg-builtins
lg-errors
lg-data-structures
lg-storage
lg-logs
lg-transactions
Expand Down
158 changes: 158 additions & 0 deletions docs/lg-data-structures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
# Data structures
In terms of data structures, Algorand Python currently provides support for
[composite](https://en.wikipedia.org/wiki/Composite_data_type) data types and arrays.

In a restricted and costly computing environment such as a blockchain application, making the
correct choice for data structures is crucial.

All ARC-4 data types are supported, and initially were the only choice of data structures in
Algorand Python 1.0, other than statically sized native Python tuples. However, ARC-4 encoding
is not an efficient encoding for mutations, additionally they were restricted in that they could
only contain other ARC-4 types.

As of Algorand Python 2.7, two new array types were introduced [`algopy.Array`](#algopy.Array), a mutable array type
that supports statically sized native and ARC-4 elements and [`algopy.ImmutableArray`](#algopy.ImmutableArray) that has
an immutable API and supports dynamically sized native and ARC-4 elements.

## Mutability vs Immutability
A value with an immutable type cannot be modified. Some examples are [`UInt64`](#algopy.UInt64), [`Bytes`](#algopy.Bytes), `tuple` and `typing.NamedTuple`.

Aggregate immutable types such as `tuple` or `ImmutableArray` provide a way to produce modified values,
this is done by returning a copy of the original value with the specified changes applied
e.g.
```python
import typing
import algopy

# update a named tuple with _replace
class MyTuple(typing.NamedTuple):
foo: algopy.UInt64
bar: algopy.String

tup1 = MyTuple(foo=algopy.UInt64(12), bar=algopy.String("Hello"))
# this does not modify tup1
tup2 = tup1._replace(foo=algopy.UInt64(34))

assert tup1.foo != tup2.foo

# update immutable array by appending and reassigning
arr = algopy.ImmutableArray[MyTuple]()
arr = arr.append(tup1)
arr = arr.append(tup2)
```

Mutable types allow direct modification of a value and all references to this value are able to observe the change
e.g.
```python
import algopy

# both my_arr and my_arr2 both point to the same array
my_arr = algopy.Array[algopy.UInt64]()
my_arr2 = my_arr

my_arr.append(algopy.UInt64(12))
assert my_arr.length == 1
assert my_arr2.length == 1

my_arr2.append(algopy.UInt64(34))
assert my_arr2.length == 2
assert my_arr.length == 2
```

## Static size vs Dynamic size
A static sized type is a type where its total size in memory is determinable at compile time, for example
`UInt64` is always 8 bytes of memory. Aggregate types such as `tuple`, `typing.NamedTuple`,
`arc4.Struct` and `arc4.Tuple` are static size if all their members are also static size
e.g.
`tuple[UInt64, UInt64]` is static size as it contains two static sized members.

Any type where its size is not statically defined is dynamically sized e.g. `Bytes`,
`String`, `tuple[UInt64, String]` and `Array[UInt64]` are all dynamically sized.

## Algorand Python composite types

### `tuple`
This is a regular python tuple
* Immutable
* Members can be of any type
* Most useful as an anonymous type
* Each member is stored on the stack

### `typing.NamedTuple`
* Immutable
* Members can be of any type
* Members are described by a field name and type
* Modified copies can be made using `._replace`
* Each member is stored on the stack

### `arc4.Tuple`
* Can only contain other ARC-4 types
* Can be immutable if all members are also immutable
* Requires [`.copy()`](#algopy.arc4.Tuple.copy) when mutable and creating additional references
* Encoded as a single ARC-4 value on the stack

### `arc4.Struct`
* Can only contain other ARC-4 types
* Members are described by a field name and type
* Can be immutable if using the `frozen` class option and all members are also immutable
* Requires [`.copy()`](#algopy.arc4.Struct.copy) when mutable and creating additional references
* Encoded as a single ARC-4 value on the stack

## Algorand Python array types

### `algopy.Array`
* Mutable, all references see modifications
* Only supports static size immutable types.
Note: Supporting mutable elements would have the potential to quickly exhaust scratch slots in a
program so for this reason this type is limited to immutable elements only
* May use scratch slots to store the data
* Cannot be put in storage or used in ABI method signatures
* An immutable copy can be made for storage or returning from a contract by using the [`freeze`](#algopy.Array.freeze) method e.g.
```python
import algopy

class SomeContract(algopy.arc4.ARC4Contract):
@algopy.arc4.abimethod()
def get_array(self) -> algopy.ImmutableArray[algopy.UInt64]:
arr = algopy.Array[algopy.UInt64]()
# modify arr as required
...

# return immutable copy
return arr.freeze()
```

### `algopy.ImmutableArray`
* Immutable
* Modifications are done by reassigning a modified copy of the original array
* Supports all immutable types
* Most efficient with static sized immutable types
* Can be put in storage or used in ABI method signatures
* Can be used to extend an `algopy.Array` to do modifications e.g.
```python
import algopy

class SomeContract(algopy.arc4.ARC4Contract):

@algopy.arc4.abimethod()
def modify_array(self, imm_array: algopy.ImmutableArray[algopy.UInt64]) -> None:
mutable_arr = algopy.Array[algopy.UInt64]()
mutable_arr.extend(imm_array)
...
```


### `algopy.arc4.DynamicArray` / `algopy.arc4.StaticArray`
* Supports only ARC-4 elements
* Elements often require conversion to native types
* Efficient for reading
* Requires [`.copy()`](#algopy.arc4.DynamicArray) if making additional references to the array

## Recommendations
* Prefer immutable structures such as `tuple` or `typing.NamedTuple` for aggregate types as these support all types and do not require `.copy()`
* If a function needs just a few values on a tuple it is more efficient to just pass those members rather than the whole tuple
* Prefer static sized types rather than dynamically sized types in arrays as they are more efficient in terms of op budgets
* Use `algopy.Array` when doing many mutations e.g. appending in a loop
* Use [`algopy.Array.freeze`](#algopy.Array.freeze) to convert an array to `algopy.ImmutableArray` for storage
* `algopy.ImmutableArray` can be used in storage and ABI methods, and will be viewed externally (i.e. in ARC-56 definitions) as the equivalent ARC-4 encoded type
* `algopy.ImmutableArray` can be converted to `algopy.Array` by extending a new `algopy.Array` with an `algopy.ImmutableArray`
Loading