Questions and answers
What Pagda is, what it promises, and what to do about the errors people hit first.
About Pagda
- What is Pagda?
Pagda is a subset of Python with dependent types. A file that imports
dependentis translated to Agda and checked by theagdaprogram before it runs. Such a file can use the part of Python the checker understands. Thepagdaprogram is also a Python 3 interpreter, written in C, and runs ordinary Python files without checking them.- What is a dependent type?
A type that mentions a value. "A list of ints" is an ordinary type. "A list of exactly
nints" is a dependent type, and so is "a proof thatiis less thanlen(xs)".- Do I need to know Agda?
No, to write programs. You write Python. Type errors come from Agda, and they use Agda's spelling for types, so the table of spellings is worth a look. Writing proofs is easier if you have met a proof assistant before, and Proofs starts from the beginning.
- How does the name come about?
Python and Agda.
Is it Python?
- Which Python programs does
pagdarun? Ordinary files run on Pagda's own interpreter. It handles functions with every kind of parameter, closures, decorators, classes with multiple inheritance, generators, comprehensions, exceptions,
with,match, f-strings, and imports of.pyfiles and packages.Its standard library is small:
math,sys,time,random,os(part),json,collections,functools,itertools,dataclasses,typing,heapq,bisect,operator,string,copy,abc.Missing:
bytes, complex numbers,async, metaclasses, and modules such asre,enum,datetimeandpathlib.- Is a checked file still a Python file?
Yes. It runs under
python33.12 or newer withdependent.pybeside it and prints the same output. There the types are not checked.- What can a checked file not hold?
Floats, sets,
finallyandwithblocks, file access, assigning to a field of an object, and inheritance between classes. The reference has the full list. Code that needs these goes in an ordinary Python file, or behind a@trusteddef.- Can checked and ordinary files work together?
Yes. An ordinary file can import a checked file and call its defs. A checked file reaches ordinary Python through defs marked
@trusted, and can import other checked files.
What the check guarantees
- What does an accepted file promise?
When a checked file is accepted and run as the main program: every call gets arguments of the annotated types, every
matchfinds a case, every loop and recursion ends, no index, key,pop, division or subtraction fails, noNoneis used as a value, everyassertholds, and the only exceptions are the ones the file raises withraise.- Where does the promise stop?
- An ordinary Python file can call a checked def with any arguments. Annotations are not checked while the program runs.
- A
@trusteddef is believed. It promises its result type, the same result for the same arguments, that it leaves its arguments unchanged, and that it does not raise. - Very deep recursion still stops at Python's limit of 1000 nested calls.
- The promise rests on Pagda's translation giving each construct the same meaning the interpreter does. The tests compare the two on edge values.
- Do the types slow the program down?
No. Checking happens before the program starts, and the result is remembered until the file changes. The program that runs is the Python you wrote.
Errors people hit first
- "The checker cannot see that a condition this line relies on holds"
The line reads
xs[i], divides, pops, or subtractsNats, and nothing nearby shows it is safe. Put it under a test:if i < len(xs):,if xs:,if b != 0:,if k in d:.- "'xs' may be shared here"
The code changes a list or dict in place, and something else might hold the same object: it is a parameter, or it came from a call, or it was given a second name. Make a copy the function owns:
xs = list(xs).- "this value may be None"
A value of type
Optional[T]is being used as aT. Test it first:if x is None: .... After the test the name holds a plain value.- "the checker has to see that a while loop ends"
The loop is not one of the two shapes the checker handles by itself. Rewrite it as a
forloop if you can. Otherwise end it withdecreases(measure, proof); Proofs shows how.Int !=< Nat, orNat != IntAn
intis being used where aNatis wanted, or a number written out was taken as aNatwhere anintis needed. Annotate the name (x: int = 0), or convert withto_nat(x)orabs(x).- "Agda could not work out a type here"
Usually an empty list or a
Nonewith nothing to say what it holds. Add an annotation:out: list[int] = [].
The project
- Is Pagda open source?
Yes. The source is at github.com/jackmitchelwidman/pagda, under the MIT licence. Bug reports and questions go in the issues there.
- How mature is Pagda?
It is new. There are no numbered releases, and it is built from source. Its test suite runs every example on this site, compares Pagda with
python3, and holds a set of programs that must be refused.- How is it built?
Pagda is about 17,000 lines of C. Most of it is the interpreter: a parser, a bytecode compiler, a stack machine, and a garbage collector. The translator to Agda is one C file of about 5,000 lines. The Agda definitions that checked files are translated against are in a single Agda module that ships inside the program.
- Which Agda does it need?
Agda 2.8.0. Pagda calls the
agdaprogram and uses none of Agda's libraries.