• Documentation
  • Why Pyret?
  • The Pyret Crew
  • Scuttlebutt
  • Source Code
See More

Pyret is a programming language designed to serve as an outstanding choice for programming education. It works effectively at many levels, with several curricula in active use incorporating Pyret. The language is under active design and development, and free to use or modify.

First Examples Curricula & Books (Course) Developers Major Features

 flag = image-url("https://.../pyret-sticker-caps.png")
blackout = rectangle(550, 425, "solid", "black")
blank-flag = place-image(blackout, 450, 285, flag)
bonnie = scale(0.75, image-url("https://.../pyret-logo.png"))
fun draw-bonnie(angle :: Number) -> Image:
  scale(0.5, place-image(rotate(angle, bonnie), 450, 285, blank-flag))
end
spinner = reactor:
  init: 0,
  on-tick: {(angle): angle + 5},
  to-draw: draw-bonnie
end
spinner.interact() 

Programming In Pyret

Pyret is designed from the ground up to run entirely in regular browsers with no installation at all, avoiding frustrating student-hours lost to installers and platform choice and IDE configuration and more. Indeed, Pyret is running right on this very page – check out the examples with the “Run It!” buttons!

Programs that create images give immediate visual feedback. Functions that operate on images enable students to learn about function composition in an medium that is both engaging and educational.

 treetop = triangle(60, "solid", "darkgreen")
trunk = square(20, "solid", "brown")
tree = above(treetop, trunk) 

 examples "cube function":
  cb(0) is 0
  cb(3) is 27
  cb(-5) is -125
end

fun cb(n):
  n * n * n
end 

Pyret provides a pleasant syntax for writing illustrative examples of how functions should behave. These serve not only as documentation, but are also useful for students to demonstrate their understanding of a problem before they start programming.


Pyret's number system supports exact rational arithmetic for many operations. This avoids having floating point as a curricular dependency early on. When approximations become inevitable, they have an explicit representation: Roughnums.


 for each(n from range(0, 10)):
  print(n)
end
squares = for map(n from range(0, 5)):
  n * n
end
# for filter, fold, and more! 

The for construct provides a familiar syntax for iterating over elements, and much more.


Pyret provides modules, with a rich set of features to control where they are found and where they are exported. Starter or support code for assignments can be imported directly from services like Github.

 import url("https://raw.githubusercontent.com/brownplt/pyret.org/...") as TTT

X = TTT.X
O = TTT.O
Blank = TTT.Blank

board-xox =
TTT.board(
    [array:
    [array: X, Blank, Blank],
    [array: Blank, O, Blank],
    [array: Blank, Blank, X]])

TTT.draw-board(board-xox) 

And much much more! Check out all the major features of Pyret!

Programs that create images give immediate visual feedback. Functions that operate on images enable students to learn about function composition in an medium that is both engaging and educational: composing images rather than numbers provides an immediate visual artifact, which makes it easier to spot errors and match a result to an intended design.

Multiple curricula take advantage of these features. In Bootstrap, lessons like Make a Flag and Hour of Code are centered around it. Likewise, DCIC also uses them for a quick and attractive introduction to programming.

Pyret's number system supports exact rational arithmetic for many operations. This avoids having floating point as a curricular dependency early on. When approximations become inevitable, they have an explicit representation: Roughnums.

Pyret provides a pleasant syntax for writing illustrative examples of how functions should behave. These serve not only as documentation, but are also useful for students to demonstrate their understanding of a problem before they start programming. The Examplar project is built entirely around this feature.

Examples can be written as part of the function definition or just after or before. Stand-alone example blocks optionally take a descriptive string, which not only improve documentation but are also helpful for understanding failures.

Pyret incorporates Tables as a basic, built-in datatype. We have argued that tables are perhaps the key datatype for early programming, and DCIC gives them pride of place, so having high-quality programming support for tables is critical.

Pyret has algebraic data types and case matching in the style of most modern languages. Direct construction, along with built-in testing, make it straightforward to incrementally build up data structure definitions.

Pyret allows you to optionally write annotations on any variable binding. These are checked dynamically.

Dynamic checking can create a lot of run-time overhead (even changing a program's big-O characteristics). Pyret uses a pleasant trade-off: it checks the outermost annotation only. This takes constant time and catches most errors, but lets a few subtler ones to be checked later.

Pyret also ships with an experimental type checker, which covers most but not all of the language's features (tables are particularly tricky). You can access the type-checker by clicking on the Run button's drop-down to select the type-checker. Observe how the default program, even with the call to s2cp commented out, results in an error!

Note: after trying out the type-checker, please revert to the regular Run button. Otherwise, you may get odd errors in some of the other feature tabs, which were intentionally not designed to pass the type-checker.

Pyret provides an extensive set of features for writing tests. You don't need a separate library or “build file” or write or run them. It's part of the programming language, because we believe it is imporant to write robust programs, and testing is a major component of that.

Reactors are a mechanism for describing interactive computations. Reactors, especially in conjunction with images, can be used to build physical simulations. These features are used in several curricula such as Bootstrap:Physics, Bootstrap:Reactive, and DCIC. They are supported by both language development and curricular research.

To understand the design, flexibility, and careful interaction of reactors with other language features like testing and tables, see our detailed paper.

In many languages, the only easy way to examine values during program execution is by using print commands, leading to what is often called “printf-debugging”. Pyret recognizes the value of examining dynamic values and provides an elegant way of doing this: spies. Unlike with a “printf”, spying presents well-formatted output: you can trace back to the source, the output looks different to distinguish from other printed output, values in the display are clickable, etc.

Pyret has full, proper, lexically-scoped higher-order functions (colloquially referred to as “lambdas”). Because they are used in many contexts, the language has three different syntaxes for it. Pick the one you find most convenient!

Pyret has “for loops”. They are more useful and powerful than those in most other programming languages.

Though many libraries in Pyret use lists, Pyret also has built-in support for sets, which are more appropriate in some settings than lists. In Pyret, sets behave as one would truly want them to: there is no such thing as the “first” element (as with lists), and instead programs use pick to extract an element.

What happens if you pick multiple times? Pyret does not want programs to get dependent on a particular behavior—sets, after all, do not have an order, but if the implementation acted as if they did, programs would grow to depend on that. Therefore, a Pick is like a “first and rest”, except there's no guarantee which “first” element you will get; the “rest” corresponds to all the other elements depending on what was chosen “first”.

Unlike in many other languages, you may have noticed that […] is not reserved for lists; instead, programs need to write [list: …] to construct a list. This is because Pyret does not want to over-privilege a single datatype; different programs depend on different collections of them, and it is nice to be able to define them comfortably.

Thus, in Pyret, several datatypes have convenient constructors besides lists: sets and dictionaries are good examples. But programs can also define their own constructors! The process is a bit unwieldy so that custom constructors also have good performance. But this unwieldy code only needs to be written once, by the constructor creator; users of the constructor do not see this.

Pyret has support for tuples, which are collections of ordered data of fixed size. Elements of tuples are accessed by position. Tuples can also be destructured at variable binding.

However, we caution against using tuples too much. We feel that tuples are best used either within a function itself, or when a called function needs to return multiple distinct values that will immediately be destructured and named. If a tuple flows intact through a program, after a while it becomes very difficult to tell what it represents, and it may even be confused with other tuples that have at least as many elements. In the latter cases, it's better to use an object with named fields, or (if the datum is sufficiently significant and visible in many parts of the program) a fresh datatype.

Pyret has both mutable variables and mutable structures. Pyret values are immutable by default (which reduces surprise and makes testing much easier), but mutation is available when needed: students just need to invoke it explicitly.

Pyret support for mutable structures is especially smart. Mutable fields are displayed with black-and-yellow striping (what some students have labeled the “bumblebee”). This indicates that the value you are seeing may change. Furthermore, clicking on the bumblebee updates it to its current value!

Modules are useful for providing new services (such as functions and types). However, sometimes users—especially educators—will want to limit the set of visible services. For instance, certain functions may make a homework trivial, so they may want to remove those functions from the set of visible services.

For this, Pyret provides contexts. A context definition is a description of the entire namespace. It starts with an empty namespace and adds to it until it has the desired contents, which it then exports. Thus a context determines not only what names are bound and not bound, but also what they are bound to.

Pyret has a very sophisticated story when it comes to equality. You can read much more about it on this page.

Pyret provides modules, with a rich set of features to control what is exported. For instance, here is a module stored on GitHub that provides some support for tic-tac-toe.

Programmers often need to define partial functions. Rather than return “sentinel” values (which might fail only much later in the computation or, worse, be interpreted as valid response and never fail at all), Pyret offers two standard mechanisms. One is to raise an exception, while the other is use the Option type.

Exceptions make more sense when dealing with truly unexpected situations and/or ones from which there is no meaningful path to recovery. Options make more sense when the situation is reasonable to expect and/or is there some meaningful value that can be provided. Both can be used with testing.

In several curricula, including Boostrap Data Science, students work with programs that load and analyze raw data. Data can be easily loaded from CSV files at public URLs, or from Google Sheets.

As programs get more sophisticated, it is often not enough to write unit tests; we need to test properties. Pyret gives you primitives for expressing the idea of values satisfying predicates.

Pyret does not, however, provide data generators for property-based testing, because writing those is usually instructive and also a good way to learn about and explore biases in data.

Pyret has objects and methods. Students can therefore transition from functions (which have a simpler calling semantics) to objects (which are more complex, but have conveniences), and eventually also define their own methods.

Curricula and Books

For grades 6-12 (secondary) students, our partners at Bootstrap are the leading designers of integrated computing curricula. All the Bootstrap curricula build primarily atop Pyret. Bootstrap covers numerous areas (with ties to algebra, data science, AI, physics, and more) and provides comprehensive materials that have been tested in thousands of classrooms and by tens of thousands of students every year. Bootstrap is also, by design, modular: you don't have to adopt a whole course, but can instead choose modules that fit best with what you're already teaching. Head over to Bootstrap to learn more!


Our book, Data-Centric Introduction to Computing (DCIC), provides a modern introduction to computing education. You can see some of the research [1, 2, 3] that has driven its design and content.

DCIC takes the position that introductory data science offers an interesting, accessible, and meaningful introduction to programming that addresses many problems that plague current introductory curricula. It then transitions from that to data structures. You can read our critique that inspired the book. Pyret offers various features that support this introduction and transition.

In particular, DCIC takes the position — driven by a significant body of literature in educational and cognitive science — that students learn programming better when they can compare and contrast related but different things. Thus DCIC teaches not one but two programming languages: Python in addition to Pyret. But the progression from Pyret to Python is (a) staged carefully to minimize difficulties, and (b) includes seeing them side-by-side. Pyret, in turn, is designed to facilitate this kind of comparison with Python


For (Course) Developers

Pyret has a Visual Studio Code extension that opens files in the same visual editor as the examples above. It works anywhere Visual Studio Code can run, including on github.dev. Read more in the docs.


Pyret has specific libraries and features for creating and deploying starter code for students seamlessly. We developed these features and workflows in our own courses, with our own TAs, and for our own curricula.


All the examples on this page embed an instance of Pyret through its embedding library. You can embed Pyret on your own websites and projects by installing that npm package, which has an API for controlling and listening to the embedded instance.

const code =  `use context starter2024
dot = circle(50, 'solid', 'red')`;
pyretEmbed.sendReset({
    definitionsAtLastRun: code,
    interactionsSinceLastRun: ["dot"],
    editorContents: code,
    replContents: ""
});

Pyret runs from the command line via the pyret-npm package. Nearly all libraries (including images) run the same offline and in-browser. This can be especially important for grading student code submissions in an automated way.


☠️ Remember: International Talk Like a Pirate Day is September 19 every year! ☠️

Site built using Pollen and Racket.