HNHacker News
TopNewBestAskShowJobs

jpolitz

451 karma · joined July 29, 2012

https://jpolitz.github.io
submissionscomments
jpolitz··on Pyret – A language exploring scripting and functional programming
There is no MOOC I know of that uses Pyret (yet).

Racket is great! If you're already deep into Racket, I wouldn't say stop to try Pyret unless you particularly have the goal of learning more languages, or there's something about Pyret that really gets you interested.

We taught a programming languages course that uses Racket back in 2012, but all the material is still online:

https://cs.brown.edu/courses/cs173/2012/

So if you're looking for online content that we recommend, that could be an interesting next step that makes use of the momentum you have in following Racket.

The Pyret book PAPL is free online, and it's possible to dive in and follow that if you're curious about learning Pyret, though it doesn't have the infrastructure of assignments with autograders, etc, that a MOOC would have:

http://papl.cs.brown.edu/2015/

jpolitz··on Pyret – A language exploring scripting and functional programming
Hey fellow Swattie! [Actually, I'm at UC San Diego as of this fall, so I'm a former fellow Swattie, I suppose :-) ]

The programming languages course in question (https://www.cs.swarthmore.edu/~jpolitz/cs91/s15/) indeed used Pyret, and that course is a heavy functional programming implementation course (writing interpreters, type inference, GC, etc).

That's using Pyret for more complex programming, and is the upper end of where we use it pedagogically right now. Ditto for CS019 (the advanced introduction to data structures), and CS173 (PL) at Brown. In all of those cases, students _are_ expected to have some prior programming experience, so they get a faster introduction to Pyret.

In the other direction, Pyret is in active use at the middle and high school level in math, cs, and physics classes, and this is where much of our active design work is tailored (tables, reactors, etc have some explicit curricular goals they target). They have scaffolding and workbooks written for their grade level, use features suitable for the context they are in, and so on.

The use in CS91 in particular is

(1) appropriate in the first place, since many functional languages could work in that setting, (2) eating my own dogfood, and (3) an excellent opportunity to discuss design decisions of a language that students are learning in a PL course, while it is still having design decisions made about it!

jpolitz··on Pyret – A language exploring scripting and functional programming
Thanks for the feedback.

> The docs you link for the "dead simple animation framework" assumes I understand templates; that's hardly dead simple

The idea of "templates" comes with a lot of baggage from C++-land. If we were doing this with C++ templates, I'd agree, because the errors are abysmal and the types are difficult to write.

As an example, we teach students to pick "the type of their reactor's state" as a useful pedagogic step in physics simulations, which is almost always a (maybe nested) tuple of numbers. But writing out the types (the instantiation of "a" in to-draw, on-tick, etc) is a useful and quite concrete activity. Since we have them working with types in their contracts already, they're equipped to think through data definitions that way.

That kind of type-based reasoning is a kind of thinking that isn't just loops and arithmetic, also needs language and curricular support, and is something we try to support early and often.

> if you did the same with Pandas in Python...

We could do more to integrate with existing, powerful libraries. Since we've found it useful to be browser-based to make things widely accessible, we'd probably be looking for similar things in JavaScript, but the point is well-taken.

That said, tables do more than work with just "half-baked" Google spreadsheets. The interface is generic to support other formats – the Google Sheets import is a library, and a Pyret/JS programmer can write library code to import from other sources (this is of course work, and won't magically appear, but the language supports more than Google Sheets by design). In addition, tabular data is built-in to the language enough to use it for _testing_ example tables (and functions over tables) with the same primitives as testing other functions. And the animation framework can spit out tables that trace execution of the animation, so students can generate their own data from simulations.

All of these things live together inside the same language that has other features we've found useful for beginners, like more authentic math (not just bignums but rationals), easy image creation, example/testing support, carefully-crafted error messages targeted at beginner vocabulary, and so on.

> I have a four-year-old now, and the other day she asked if I could count to a thousand million. I answered "Nope, but I'll show you Mr. Fortran, he can."

That's just awesome :-)

jpolitz··on Pyret – A language exploring scripting and functional programming
Yes! You can contribute to the Bootstrap project directly:

http://www.bootstrapworld.org/contribute/index.shtml

jpolitz··on Pyret – A language exploring scripting and functional programming
Thanks for the advice! What are some examples where a teaching language has followed these points and succeeded?

What is the weirdness and/or pet features you see in Pyret?

jpolitz··on Pyret – A language exploring scripting and functional programming
This is useful feedback. I think this pair of comments combines to give a decent answer:

https://news.ycombinator.com/item?id=13186997

https://news.ycombinator.com/item?id=13187379

jpolitz··on Pyret – A language exploring scripting and functional programming
Perhaps `unfun` can be incorporated into this:

http://blog.brownplt.org/2014/04/01/var-vs-yar.html

jpolitz··on Pyret – A language exploring scripting and functional programming
The readability, unintuitive, and "odd syntax" comments are somewhat subjective, though I'd curious to hear what parts are odd and unintuitive for you.

I linked to a perspective on where Pyret fits in in another similar answer:

https://news.ycombinator.com/item?id=13187379

We also may have different definitions of what a "beginner" needs for community and help.

Pyret is used in middle and high school classes (along with the undergraduate level). Those students, nor their teachers, are likely to know what IRC is or how to seek help on it. We do a lot of curriculum development, and professional development workshops in the summer are a major way we interact with these teachers and set them up with a high-bandwidth way to communicate with us over the year (which is direct email or mailing lists).

Prompt and detailed help is certainly available for teachers (who then help their students), and it's coming from the folks who designed the curriculum and the language together. Those mailing lists are public (pyret-discuss [1] and bootstrap-teachers [2]), and anyone is welcome to ask away.

[1] https://groups.google.com/forum/#!forum/pyret-discuss [2] https://groups.google.com/forum/#!forum/bootstrap-discuss

Of course, Pyret doesn't have the huge community, Stack Overflow presence, documentation maturity, etc. of Python (or other major languages) for general programming. That takes time and consistent effort, but the lack of it doesn't negate the reasons Pyret is good for its current use cases.

jpolitz··on Pyret – A language exploring scripting and functional programming
This paper (http://cs.brown.edu/~sk/Publications/Papers/Published/fffkf-...) linked from "Why Pyret" (http://www.pyret.org/pyret-code/) is a long answer to "why teaching languages."

It may also be useful to know that Pyret exists as part of a larger project and community. We're using Pyret to develop curricula for Bootstrap (http://www.bootstrapworld.org/), which integrates computing curricula into existing middle and high school classes, like math and physics.

Part of the idea is to integrate computing into these settings to reach students who may not self-select for computing.

A lot of Pyret's design, like a dead simple animation framework (https://www.pyret.org/docs/latest/reactors.html#%28part._to-...) and interfacing with real spreadsheets (https://github.com/brownplt/pyret-lang/wiki/TM028-Pyret-and-...), supports these low-friction uses of computing in these curricula.

Having these features and concepts baked into the language, supported by the IDE, and easily accessible goes a long way to building something that a non-CS teacher can pick up and use in an existing class.

jpolitz··on Pyret – A language exploring scripting and functional programming
Pyret's runtime is entirely built on JavaScript (seriously: you can load https://code.pyret.org/editor, then turn off the network, and your IDE still works), and Pyret programs compile to JavaScript. At the command-line, it builds standalone JavaScript files that run under node.

It's entirely possible to write pure JavaScript libraries that interface with the language (the compiler expects AMD style and requires that you write some small wrapping code). That's how the image library (https://www.pyret.org/docs/latest/image.html) works, for instance, and how we connect to Google Sheets (https://github.com/brownplt/pyret-lang/wiki/TM028-Pyret-and-...).

Industry devs should be suspicious of Pyret's performance right now, though that will improve as time goes on. We've been spending most of our effort supporting our student and teacher users, so things that really build the traditional out-of-the-box language experience aren't where a typical application developer would hope.

I think that's fine since we've made our intentions and goals clear (the educational audience and a coherent curricular design), and would be less fine if we were offering Pyret as a general-purpose language today. Offering it as general-purpose will become more and more reasonable as time goes on.

jpolitz··on Pyret – A language exploring scripting and functional programming
Thanks for the feedback – we've gotten this a few times, and I think the homepage is targeted too much towards getting across Pyret's design to an already multi-lingual audience, and not enough towards actual beginner uses of the language (of which we have many).

This is something we're working on changing to make it easier to see what true beginner examples look like.

jpolitz··on Pyret – A language exploring scripting and functional programming
This is very difficult to argue empirically, and I don't claim to know what's going on in students' minds. In particular, I don't know what "most people in the world" struggle with, just those students in the middle, high school, and undergraduate courses that I've been involved in.

As a concrete counterpoint of _friction_ between what students may have as background and what many languages do that relates to language choice:

Many math teachers we work with use the word "variable" to refer to a name that can change per-instantiation of a function or expression by substituting a value. But it doesn't make sense mathematically for x to be 5 "now" and 6 "later". So writing

    x = 5
    x = 6
is in some sense expressing an unsolvable system of equations.

Math variables and traditional (mutable) computer science variables are very different things. This provides an _immediate_ tripping point in vocabulary if you want to, say, teach computing in a math or physics class (or leverage that background). So we make a conscious choice in Pyret to dissuade multiple definition of the same variable, or mutability of a variable, to hew close to definitions we can leverage without creating extra conflict and confusion between concepts.

That particular point may differ in different contexts, and deeper studies are definitely needed to figure out what kinds of "notional machines" are easiest or best for students to build up. There may even be more to say about addressing this particular example. But this is a little perspective on concrete reasons for some of these choices.

jpolitz··on Pyret – A language exploring scripting and functional programming
Pedagogically, the nice thing about the examples/tests just being code for beginners is that they are... just code!

Once we've taught students about function calls and values, it's a small jump to write an example with "is" in the middle. The syntax errors, static errors about unbound identifiers, and dynamic behavior all act the same as everywhere else in the program, so it's less friction to write the first test.

For getting off the ground writing that first test, I've been really happy with what Pyret lets us do. There is more involved in getting better testing/reporting options as systems scale up (we have lots of test-only files for the compiler), but pedagogically, the testing block infrastructure has worked well.

jpolitz··on Pyret – A language exploring scripting and functional programming
I responded to a similar comment in another thread from a pedagogic perspective, I hope this answer gives some perspective on how we approach this point:

https://news.ycombinator.com/item?id=13186216

Also, Pyret can be used to make things – Pyret's compiler is written in Pyret!

As the language matures, the experience of writing small Pyret programs that grow into real applications will only get more authentic.

jpolitz··on Advice on learning Python efficiently
It's true, Pyret gets ideas from a handful of places, not just Python.

From a student or instructor's point of view, it is serving a lot of the same ends as some of Python's choices:

Toplevel code runs as a script without ceremony, the default behavior doesn't statically check types but is conservative about coercing types and overloading operators (e.g. "a" + 5 is an error), integers promote to bignums by default (though Pyret supports exact rational bignums as well).

Pyret supports docstrings explicitly, and examples/check blocks have similarities to Python's doctest.

Fields of ADT instances can be accessed simply with "." (in contrast to OCaml or other functional languages), and methods close over self on dot-access as they do in Python.

Some of these things are also true of Ruby, so that's a fair comparison as well. And things like "data", and the gradual type-checking facilities, are clearly coming from other sources (though Python is moving in a gradually-typed direction as well).

I know we were thinking about Python in particular when we made a lot these decisions, so I think it's a fair characterization, though Python is one of several inspirations.

jpolitz··on Advice on learning Python efficiently
I'm curious which parts look (more) complicated to you.

For some perspective, when teaching Pyret to total beginners, we start with arithmetic and calls to library functions, then build up to function definitions and examples. At that point, there's just a handful of syntactic constructs to consider, and they are nearly identical to Python.

Things like "data" and "cases" don't come till later, and do come with more overhead, but also are introduced to students who already have some "finger-feel" with the language. Python has its own overhead with "class" for defining structured data at a similar point, including things like "self" and "__init__".

jpolitz··on Advice on learning Python efficiently
Pyret (and the Bootstrap curricula in particular, where we use both Scheme and Pyret [http://bootstrapworld.org/]) really strives to avoid the purely "bottom up" approach.

Curricula in Pyret focus on building games using concepts from math, or interactive simulations using concepts from physics, or data analyses using concepts from working with spreadsheets.

I agree that a purely "CS concepts"-based approach is dry and ineffective for a huge portion of students. That's why applications are so critical, and why we build things like tables (https://twitter.com/PyretLang/status/773605473824145408) into the language, so we can give students real-world tasks to do right away.

We happen to also have carefully designed the language to allow us to teach these applications along with a rigorous programming style. Students write _examples_ that get them ready to understand automated testing, they write _contracts_ that teach them about type-based interfaces, they write _data definitions_ that teach them about structured data, and so on. But it's always in service of an application.

So I very much agree that a purely "bottom up" CS-concepts-only approach has issues.

jpolitz··on World Chess Championship: Game 2, Draw
Also a plug for ChessNetwork, who posts 20-min summaries of games the day after, for example:

https://www.youtube.com/watch?v=1vgn7OXug3M

jpolitz··on React Implementation Notes
Self-reply for posterity. Here's a sketched-out comparison of generators vs. the manual strategy we use in Pyret (this isn't _exactly_ what Pyret generated code looks like, but it's close):

https://www.measurethat.net/Benchmarks/Show/675/0/stack-stra...

As of October 2016, generators are much slower on all browsers I tested.

jpolitz··on React Implementation Notes
> doesn't every recursive function have an iterative version

True in the abstract, yes. But it's a sophisticated compiler indeed that turns something like a recursive binary tree traversal into a loop (it would need to synthesize the stack worklist).

In practice, it's easy to do this for tail recursion (and mutual tail recursion, with a little more sophistication). You can get slightly fancier with "tail recursion modulo cons," which is a little more clever and handles map. Beyond that, it's pretty gnarly to do a good transformation, because recursive code is implicitly using the stack in interesting ways.

> couldn't... functional primitives be converted to imperative code

Indeed, and we do write those in pure JS with carefully-crafted while loops to make those primitives more efficient. But if students are learning to write their own map, or another functional combinator on lists, those need to work, too, and will be implemented recursively by them.

> Generator functions are no different than regular functions when it comes to stack depth.

Yeah, stack depth in general is annoyingly low on modern browsers, IMO, so this isn't just a problem with generators. It's also unpredictable (http://stackoverflow.com/a/28730491/2718315). So we're working around the normal stack limit already. I was sort of hoping that when a generator's continuation was captured, it would stay heap-allocated and not "count" towards stack space when restarted, but that's not the case.

jpolitz··on React Implementation Notes
Thanks for the good vibes!
jpolitz··on React Implementation Notes
No problem. Thanks for asking bluntly about generators. It made me write some more experiments using them.

Right now, the main thing that I think stands in the way of them being a good solution for Pyret is that they have limited stack depth – about 7000 frames on Chrome Canary, for instance. One thing we get out of our strategy, which reifies stack frames onto the heap when pausing, is that we can simulate a much deeper stack.

See http://imgur.com/a/GaBg2 (I don't need to be able to do sum of ten million, but 10,000 would be nice! This is just so a non-tail-recursive map works over reasonable-sized lists; we'd rather not have the concept of tail recursion as a curricular dependency for working with that size of data.)

jpolitz··on React Implementation Notes
I don't know what you mean by "it doesn't really matter." Surely there's a cost to using generators instead of regular function calls and returns, right? That's where the overhead would come from, because generators aren't free.

Right now, Pyret and GopherJS (the last time I checked in GopherJS's case) basically manually encode the `IteratorResult` type, and check for "stack unwind" vs "regular result" when each function call returns, if it might pause.

The first question for generators is if they are less overhead than this manual process. There's a bunch of other details, too, but this is the main one. And generators are certainly going to cause _some_ overhead over regular calls and returns, just like the manual checking of return values has overhead.

My original comment was about the lack of something like delimited continuations in JS, which would allow saving portions of the stack while intentionally minimizing the overhead of regular function calls. That's a well-fitting language-level solution to this issue.

jpolitz··on React Implementation Notes
My conjecture: Scheduling with priority is pretty easy across these systems. The dependency graph transfer would be outside the scope of what they already tackle, and require new engineering.

In Pyret, I prototyped virtual threads at one point, and each thread had the same amount of "fuel" before yielding (at the top of every compiled function in Pyret, there's a decrement to a "fuel" counter, and when it reaches zero, the stack is unwound). That could easily be configured on each start/restart of a thread to provide different amounts of fuel, or to re-order the restarts based on typical thread-scheduling policies.

Whalesong does the same thing with fuel, so I imagine it could be extended similarly.

I don't know how Doppio and GopherJS do scheduling in detail, but here's where I'd start looking:

1. GopherJS looks like it's a simple queue based on the code around https://github.com/gopherjs/gopherjs/blob/master/compiler/pr... (goroutines push themselves onto a list when they pause, then the next one starts up again)

2. Doppio implements a thread pool abstraction, so you can simulate JVM threads in the browser: https://github.com/plasma-umass/doppio/blob/master/src/threa...

jpolitz··on React Implementation Notes
The main difference is that all of the use cases I mentioned necessarily don't distinguish between calls/functions that may pause, and calls that don't (it's just the semantics of those languages that arbitrary calls might need to pause). So to use generators as a compilation target, every function has to be a generator, and every call a generator instantiation followed by yield*.

I actually don't know if that qualifies as "staggering". Your sibling comment has some truth; I can only speak generally about Gopherjs and Doppio, because I know them less intimately, but I know that Pyret and Whalesong were definitely started before generators had widespread adoption. Compiling to generators, rather than to the handwritten stack unwinding we have, is on my list of things to try and measure.

Maybe "significant" overhead would be more obviously true than "staggering," since I don't have clear numbers to back it up.

Does that make sense?

jpolitz··on React Implementation Notes
Part of these notes, Fiber [0], reminds me of a half-joking "corollary" to Greenspun's Tenth Rule (credit @shriramkmurthi):

  Any sufficiently complicated JavaScript program contains an ad hoc,
  informally-specified, bug-ridden, slow implementation of delimited
  continuations.
Control over continuations and the stack is our main compiler and runtime engineering hurdle in Pyret. Projects like Doppio [1], WeScheme [2] and GopherJS [3] go through staggering amounts of overhead and effort to get pausable, resumable, and stoppable computation in the browser environment.

I'm excited to see how this develops in React with fiber. It's much more application-specific, but it's the same underlying problem.

[0] https://facebook.github.io/react/contributing/codebase-overv...

[1] https://github.com/plasma-umass/doppio

[2] https://cs.brown.edu/~sk/Publications/Papers/Published/yk-wh...

[3] https://github.com/gopherjs/gopherjs#goroutines

jpolitz··on A Case for the Pyret Programming Language
Thanks for the kind words!

Firefox performs better than it did, because the whole language has gradually increased performance, but still the worst of the major browsers. It's worth writing down a bit about that here, just for others to see and comment on.

We have some ideas that we suspect will mitigate the main issue in Firefox, which is that Firefox stack frames seem to be very large for compiled Pyret functions. This matters because Pyret's execution control works by (a) figuring out how many "typical-sized" stack frames fit before a stack overflow; this is a little tricky [1], (b) leaving breadcrumbs of metadata on each stack frame, and collecting them on an exception thrown when the limit is reached, restarting after yielding control to the browser's event loop. The issue is that the "typical" frame size for a Pyret function in Firefox means we only get a few hundred frames, and most idiomatic Pyret is implemented recursively, hence lots of pausing and collecting the stack.

It's clear that good support for safe-for-space tail calls is a simple way to mitigate a lot of the issue. We have a version of safe-for-space tail calls that avoids using unbounded space, but doesn't help (much) with speed.

Upcoming safe-for-space tail calls in JS provides one avenue for this that we'll be trying to take advantage of.

Actually compiling instances of Pyret tail recursion to JS loops is something we want to get working. It's a mildly nontrivial engineering challenge in the presence of dynamic annotation checks for return values (which muddy the definition of "tail call", especially since annotations can contain refinement predicates -- more function calls!), the desire to report faithful error information to students, and wanting to yield to the event loop often enough to not lock the page and get "busy script" errors. It's not extremely difficult, there's just enough tricky bits that it's on a branch right now and we're not totally satisfied with it yet.

Since we've largely focused on design of language features rather than performance for a long time now, there's somewhat of a backlog of these kinds of straightforward performance ideas that aren't in the system yet.

[1] http://stackoverflow.com/a/28730491

jpolitz··on A Case for the Pyret Programming Language
Thanks for the feedback!

To add to what you said, this is exactly the kind of situation where we design features that _may_ not scale well, but are remarkably useful in teaching contexts.

Having test cases inline with code is massively useful:

1. For students not used to thinking about their program being split over several files. If tests must be in their own file, then understanding multi-file programs (modules, essentially), becomes a curricular dependency of writing tests. That is a _lot_ of friction early on – think about an intro student who has a fuzzy notion of a "working directory" or IDE "workspace" in general. Even if the eventual goal is to address these concepts, they shouldn't have to come first just to write a unit test.

2. When live coding in front of a class. Having tests in a separate file is a huge pain because you pay either (a) the cognitive overhead of switching buffers, or (b) precious projector real-estate on having both open. Since live-coding programs that are less than 100 lines long is a major use case for Pyret, this is a big win.

As programs scale up, for example in Pyret's compiler or in larger assignments, we end up with a blend of test cases in inline where: blocks, with the majority in separate files in standalone check: blocks. So at scale, things turn into more traditional-looking separate suites.

jpolitz··on Blue. No, Yellow
Another important metric: a handful of C programmers can write arbitrarily more buffer overflows than thousands of Java or Ruby programmers.
jpolitz··on “Extremely angry with the state of academic CS research right now”
It is a real issue. The programming languages and software engineering communities have been working on evaluating software artifacts for a few years now:

http://www.artifact-eval.org/

This doesn't address open access, but it does make sure the software is usable by a non-author to reproduce the results of the paper.

← PreviousPage 2 of 4Next →