Trying to create my perfect Racket for AP CS Principles - will someone take pity?

So, to review, I'm teaching AP CSP. About 40% of the course is supposed to cover programming and algorithms, and they publish their own pseudocode language, since the language the course is taught in is left to the instructor. As you might expect, the pseudocode is very imperative, mutative, and sort of like dumbed-down Java or C.

I've actually written (following Beautiful Racket) a #lang that handles this language, although the indentation and error messages could be much better. GitHub - toddobryan/apcsp-pseudocode: An implementation of APCSP pseudocode as a DSL in DrRacket

Here's the reference sheet on the language: https://apcentral.collegeboard.org/media/pdf/ap-computer-science-principles-exam-reference-sheet.pdf (which you may be able to see once CloudFlare is fixed).

I would really like to create my own customized version of teaching languages. Like most of us, I'm very opinionated about what I think works best with my students and how I need to teach content given the requirements of the AP course.

Here's the thing. I've spent about 10 hours over the last couple of days looking at both the htdp and deinprogramm repositories as models, and I'm just lost. So much of how things work is based on convention and while the docs are excellent, they seem to assume you have a general understanding of how everything works and just need to remind yourself about particular concepts. For example, I only discovered last night that signature means two things--one in relation to units and another in the teaching languages.

Would someone be willing to take me by the hand and walk me through this endeavor? My problems are myriad--I can only work on this for an hour or two at a time, except on weekends; I'm by no means a proficient Racketeer; and, I occasionally have to abandon what I'm doing for several days to deal with things like creating tests and grading assignments.

We could either do this on here as a series of messages or as email chain that I'd be happy to post a summary of back here.

What I'd eventually like to have is a progression of languages that I could do

#lang apcsp/pseudocode
#lang apcsp/bsl
#lang apcsp/isl

but in the spirit of iterative development, I'd just like to focus on #lang apcsp which would be my customized version of BSL.

HtDP and DeinProgram grew, uh, organically.

Let me recommend the following.

  1. Design your teaching language levels. My guess is that this is probably an exercise in syntax not systematic design (a la HtDP and DeinProgram). If I am correct, write down purpose statements (why this language level, what does it achieve), the grammar, sample programs, sample outcomes, sample error messages. (Oh yes these are the first three steps of the design recipe.)

  2. Next, outline the modifications to the parser you have. (Call it a template if you want.)

  3. Implement the parser, target the existing backend.

  4. Make sure to equip the implementation with an integration test suite based on the sample programs. (OK, this is step 6.)

Now, once you have done this for two languages, apply the abstraction design recipe from part III of HtDP/2e:

— what is common
— what is different
— how can you abstract functionality over the differences
— where do you need to abstract syntactically over the differences. 

That’s actually behind the arrangements of the existing teaching languages, but the evolution path was somewhat less direct. You might end up with a better implementation than ours.

;; - - -

The connection to the IDE (DrRacket) is a totally different story. While many people think of this relationship as part of the PL, I’d say it’s on the periphery and you might be better off embedding your languages into VSCode, by modifying an editing package for Python and combining it with the Racket package so that students can run it “natively” in this IDE. (You might even be able to connect it to AI later and teach students its responsible use.)

— Matthias

Oh, I'm totally into s-exp for the grammar. I just want to make some tweaks.

  • earlier introduction of local to encourage students to embrace DRY (don't repeat yourself) in functions.
  • including readline and display, since they play such a big part in the pseudocode
  • adding some checks as a post-parsing step (make sure every function has a signature and test cases--I have some stubborn 14-year-olds)
  • changing '() back to empty and #t and #f back to true and false (I'll never get to quotes or symbols, so the foreshadowing is lost on my students, and true and false are the values in both the APCSP Pseudocode and the Java that the students have to take next year.)
  • I really like the way that Dein Programm doesn't build in cons, but has students define it. That would be very cool. I want to include more of the signature work from Dein Programm, but obviously don't want the error messages in German. :slight_smile:

And then I'm going to need to add some utilities so that they can write their "Create Performance Task". The requirements are that the program include input while the program is running, a list that supports algorithmic abstraction (in other words, a list that couldn't be replaced by a small fixed number of variables), and a student-written function that includes sequencing, selection, and iteration (or recursion).

universe works for most of these, but I'm thinking it would be cool to add basic GUI things like inputs and buttons so that students can write programs that are more text-based but still make it clear to the graders that they're getting input from the user while the program is running. (Many of these points on the exam are based on a video that the student uploads showing the program running.)

(I've also thought of some esoteric functions that could display the signatures of functions and the types of values in the REPL, if that's even possible.)

Basically, I need to understand how this all fits together so that I can tweak it.

If S-expressions are okay, consider the syntax-spec package. It enables someone like you to specify the grammar and binding structure of a language easily, write a "compiler" that's essentially a standard macro, and doing this for several "levels" is also easier than doing it all by hand.

HtDP enables I/O programming in a systematic way via 2htdp/batch-io. It also comes with an "abstraction" package that includes (restricted versions of) Racket's for loops.

Mike has a teach pack for hierarchical GUIs that also matches the basic design principles of HtDP, but I suspect the docs are in German.

Otherwise your requirements are a bit nebulous.

Oh, they're very nebulous. But I can't do anything unless I can wrap my head around how all the pieces go together.

For example, I know that if a module contains a lang/reader.rkt module, then it can be used as a #lang argument. But then there's something like this in DeinProgramm at (deinprogramm-lib/deinprogramm/sdp/beginner/lang/reader.rkt)

#lang s-exp deinprogramm/sdp/private/reader
deinprogramm/sdp/beginner
'beginner

And I'm guessing that this means we're using an s-expression language whose reader is at deinprogramm/sdp/private/reader, but then I'm not sure what the relation to deinprogramm/sdp/beginner and 'beginner is.

I seem to be continuously hitting these kinds of roadblocks where I probably just need a 5-10 minute explanation of how the piece I'm staring at fits into the big picture. It's like I'm one of the blind men describing the elephant, except that, even though I can feel any part I want to, I can't suss out which part is connected to which and how.

s-exp is a generic library that does a rather simple job:

1.3.19.1 S-Expression Reader Language

It is not Mike’s.

Part 1: You don't need a language.

Your bulleted list (message 3) contains a variety of different levels of requests.

  • Begin with ISL and you will have local available.
  • Learn how to require and provide and you can introduce readline and display when you want. Have the students (require apcsp) to set up definitions that you want.
  • Re: cons - walk before you run. Also, my experiences introducing cons to beginners are uniformly bad. (Have you tried it? Don't invest too much energy until you do.)
  • The changing of true, false, and the way empty lists print is high-effort trivia if you do it yourself. Fortunately, DrRacket/BSL/ISL has options for this. Open "Choose Language" and click "Show Details" in the lower right. Constant style is the option you want to change.

It might help to spend some time reading the files in htdp-lib/htdp and htdp-lib/lang/private/teach.rkt.

Enforcing the requirements for having tests is indeed easier if you have a language so you can add stuff in the expansion. They issue is that you need to know how to do the checking before you add that code. I'm not comfortable commenting on this, but I also think it's a long ways past where you are right now.

Part 2: Really want to make a language?

I want to second the big boss's opinion here: make an example showing what you want the language to look like and what you want it to do.

You need a way to begin in little pieces.

The way to design a language like this is most emphatically not to begin with the idea that you are going to write "#lang apcsp". At least in my medium-level understanding, the #lang is changing the surface syntax and gluing together parts that you already have. In a way it's the last thing to make.

Instead, get a better understanding of what you want. Just say you're going to begin with BSL and force students to write all their pseudocode in S-expressons. What else do you want? Are you happy with that? You need a specific list of what you want to change. The less you change, the easier it will be.

Please take this with good intentions, but I'm going to call out something I saw. When experts are out of their comfort zones, they act just like every other beginner. You tell your students to write check-expects and signatures, but they don't do it. Partially because they don't see the value in it. MF told you exactly how to build a new language, but it seemed like you didn't engage. Is that also because you didn't see the value in it?

FWIW, HtDP implements this by using a (legacy) library that “formats” a given value before printing it:

In short, with appropriate parameter values like booleans-as-true/false, print-convert would format the values appropriately so write produces the desired result.

#lang racket
(require mzlib/pconvert)

(parameterize ([booleans-as-true/false #t]
               [constructor-style-printing #t]
               [abbreviate-cons-as-list #t])
  (equal? (print-convert (list #t #f))
          '(list true false)))


(parameterize ([booleans-as-true/false #t]
               [constructor-style-printing #t]
               [abbreviate-cons-as-list #f])
  (equal? (print-convert (list #t #f))
          '(cons true (cons false empty))))

Sorry. I realized that. And I've read that portion of the documentation 4 or 5 times. And I'm still somewhat lost. Here's my thought process.

#lang s-exp deinprogramm/sdp/private/reader
deinprogramm/sdp/beginner
'beginner

corresponds to

#lang s-exp module-path
form ...

where

module-path = deinprogramm/sdp/private/reader
form ... is (list deinprogramm/sdp/beginner 'beginner)

which is equivalent to

(module 'reader deinprogramm/sdp/private/reader
  deinprogramm/sdp/beginner
  'beginner)

Now it gets complicated. I gather that a module is created by first requiring the code in deinprogramm/sdp/private/reader.rkt and then evaluating the code in deinprogramm/sdb/beginner.rkt and then 'beginner is evaluated.

OK, so if I'm right, this is a complicated, but reasonable for abstractness reasons, way of letting me get access to all the stuff in deinprogramm/sdp/beginner.rkt evaluated in the context of deinprogramm/sdp/private/reader.rkt by typing #lang deinprogramm/sdp/beginner. Correct so far?

But then what does the symbol 'beginner at the end of the module do, if anything?

I get pretty far, but then I get lost in a lack of background knowledge. :frowning:

I think I've confused people by convoluting two things.

I've already made a DSL implementing the APCSP Pseudocode that mostly works. (I get occasional pink errors when things actually parse, the indentation doesn't work quite correctly, and the error messages are awful.) I implemented it so that students could run the pseudocode on a machine as well as in their heads. The code is at https://github.com/toddobryan/apcsp-pseudocode, you can install it with raco pkg install --type github toddobryan/apcsp-pseudocode and use it with #lang apcsp-pseudocode.

Here's a sample program:

#lang apcsp-pseudocode

i ← 0
REPEAT UNTIL(i > 5)
{
    DISPLAY(i)
    i ← i + 1
}

which displays

`0 1 2 3 4 5`

in the interactions window when run. It needs some work, but it's good enough until I have time to deal with it again.

Under no circumstances do I want to use this pseudocode to actually teach programming!

The language that I want to use to teach programming is almost exactly like the HtDP teaching languages, with some small changes. But I'd like to make it possible to make bigger changes, so I really want to understand how it's all put together, not just make something work that isn't extensible.

To answer a couple of suggestions, I don't want to start with ISL to get local, because ISL also prints lists using list rather than cons, and includes a bunch of other things. I like students to deal with the nested structure for a while. (And to answer another question, I taught a fairly orthodox version of the HtDP curriculm for about 10 years, so I know I want to introduce cons before list.)

I also want to get better at Racket. Maybe recreating the HtDP languages isn't the best project to start with, but it's the one that I have a use for, so seemed like a good place to start.

Oh, this is awesome. I didn't know that was there. (Unfortunately, different preferences can cause a huge headache for making sure that students see the same behavior on all machines they work on.)

I guess I have to add something like this into a require-able module and I can get that stuff for free.

Thanks for the pointers!

That's part of SDP internal implementation. It's how SDP specifies the language level:

I think this is only discoverable from

which, comes from how Racket handles #lang deinprogramm/sdp/beginner.

Specifically, when reading a module that begins with #lang deinprogramm/sdp/beginner, Racket tries to look for a reader submodule and/or the module deinprogramm/sdp/beginner/lang/reader.rkt. Then the code above comes up.

Thanks for being very tolerant of my under-estimation of your level of accomplishment. (And lack of reading skills, since it's clear in your first message what you have already done.)

I just want to second the idea of using VSCode. I use DrRacket and I am even exploring the impact of updating all the core native libraries that DrRacket uses.

Having experience with VSCode is increasingly a must have nowadays. If there was debugging (via the debug adapter protocol) and macro stepper integration available in the VSCode extension, I'd switch without hesitation. I would love to help to make this happen, but I'd need a guide to the heart of debugging in racket and all the additional help that could be mustered.

Nathan Dykman