On TEAL and PyTEAL successes and pitfalls
Algorand is an attractive network. Cheap fees, a proven stability record and a commitment towards evolution are the signs of an amazing future. Yet, there’s a big thorn on the platform: building contracts requires assembly programmers.
In this post series we will discuss blockchain evolution, abstract syntax trees, code generation, virtual machines and our vision of an accessible and modern network. Let’s start with smart contract programming and how we got to where we are!
TEAL is weird
If you didn’t know: blockchains are weird. Programs there run in a constantly changing environment. Code is added and removed at the same time it’s used. Cooperation between programs has to be done with incredible amounts of care (and a lot of times, in a “trust no-one” way).
This is not the weird part of TEAL. Not really.
TEAL was born as a decidedly non-turing-complete language[1]. Evolution has transformed it into what we use today.
The original TEAL idea
So, How do you build a non-turing-complete language that is “useful enough”?
- You cannot jump back (programs that only jump forward always end 🧐)
- No subroutines (may enable recursion, and that’s like loops. Heresy! 💀)
- No contract-initiated transactions (if you need that just add those to the transaction group and slap a validator at the end)
- Small amounts of storage (scratch, global/local storage… keeps fees low!)
- Only direct addressing when working with scratch variables (keeps contracts simple)
This resulted in a programming environment that was incredibly peculiar and really difficult to program for.
TEALv4, and beyond
TEALv4 broke this trend: callsub/retsub and backwards jumps were added. After these changes the road to a better TEAL was unstoppable: raised limits, “unlimited” memory allocation mechanisms, programmatically created transactions, even better subprocedures!
But, …Teal is still an assembly language. A lot of unnecessary complexity arises. This doesn’t make for better programmers, just a more cumbersome workflow. Can we combat this with tools?
PyTEAL: the official improvement
PyTEAL is a python library that allows developers to write smart contracts by way of composing python objects. Each TEAL instruction has a corresponding object to represent it and its arguments. Some notational improvements are included, so programmers write:
Int(3) + Tmpl.Int("CONFIG_VALUE")
…instead of
Add(Int(3), Tmpl.Int("CONFIG_VALUE"))
PyTEAL solves the stack fiddling of writing assembly but the focus on composition results in foreign semantics (if you try to think of the code as python code). Still, a TEAL instruction that takes 4 things from the stack and pushes one thing to the stack will probably look like Isn(arg_0, arg_1, arg_2, arg_3)
in PyTEAL.
The way PyTEAL generates code is overly simplistic. Instruction count may suffer a lot if the developer does not read the generated TEAL code.
If we want to understand code generation we should start with some examples. What’s the generated code for this PyTEAL snippet? Choose your answer from the table below!
a = Int(3) + Int(4)
return a + a
int 14 return |
int 7 dup + return |
int 3 int 4 + dup + return |
int 3 int 4 + int 3 int 4 + + return |
Sadly, the last one. PyTEAL only sees the computation as a tree of nodes and flattens it the most naive way possible.
What’s the value returned by this PyTEAL program?
init_a = a.store(Int(0)) # a = 0 inc = Seq([a.store(a.load() + Int(1)), a.load()]) # inc = ++a res = inc + inc # res = inc + inc return Seq([init_a, res])
The answer is 3!
inc gets evaluated twice, the first time returns 1 and the second time returns 2
. While this should not be surprising for an experienced PyTEAL developer it is still a difficult thing to wrap your head around.
PyTEAL is just a macro tool
The PyTEAL documentation clearly states: “A PyTeal program is a PyTeal Expression composed of other PyTeal Expressions. […] While only PyTeal Expressions may be included in the Expression tree representing the program, the Expressions may be generated as part of running the Python program.”.
PyTEAL is a set of tools for manipulating and building the AST of our contracts. An AST is a tree-like structure that represents syntactic properties of a given program text. Syntax in the same sense as “Syntactic Analysis” from highschool.
These tools are useful for compiler savvy developers but fail on the rest of us. The PyTEAL tree of the previous program may help a bit. Nonetheless, we believe this approach to contract programming is not a good way to write programs.
Conclusions
Tools like PyTEAL (and frameworks like Beaker) are really useful. Still, coming up with better alternatives will benefit all programmers and lower the entry barrier to the Algorand ecosystem.
Next up on this series we will discuss a possible future: Building PyTEAL programs using pythonic syntax (and our proof-of-concept python-to-pyteal transformer).
- A turing-complete language is a language where “any program can be written”.