← Back to list

A Guide to Programming with DrRacket

Learning to code with DrRacket? Here’s an unofficial starter guide to Beginning Student Language (BSL), Intermediate Student Language…

Justin Chae in Level Up Coding · 2020-08-16 16:56 · 90 claps · 25.9 min read paywalled
#drracket #functional-programming #programming #guides-and-tutorials #racket
Open on Medium ↗
Wiki topics: EDU · Education & Learning 💻 · Programming

A Guide to Programming with Racket (DrRacket)

Learning to code with DrRacket? Here’s an unofficial starter guide to Beginning Student Language (BSL), Intermediate Student Language (ISL), ISL with Lambdas (ISL+), and Racket.

Photo by Wengang Zhai on Unsplash

Photo by Wengang Zhai on Unsplash

There’s No Love for Racket

There’s no love, that is, for Racket the programming and teaching language — at very least, that’s the vibe I get from a recent Medium.com search. For example, the top stories for Racket are mostly about corruption schemes or extortion. However, somewhere in the top five search results for Racket, there ought to be something about the basics of program design or functional programming.

My friends, it is time to lift DrRacket out of the basement of programming blogs with more content on BSL, ISL, ISL+, and Racket.

In this Story

A starter guide to navigating DrRacket with Beginning Student Language (“BSL”), Intermediate Student Language (“ISL”), ISL with Lambdas (“ISL+”), and Racket to help students spend more time learning and less time wrestling with everything else. Working code examples within.

  1. Getting Started with DrRacket
  2. Overview of BSL, ISL, ISL+, and Racket
  3. Getting Started with BSL
  4. Getting Started with ISL
  5. Getting Started with ISL with Lambdas (ISL+)
  6. Getting Started with Racket

Photo by National Cancer Institute on Unsplash

Photo by National Cancer Institute on Unsplash

[embed]Getting Started To get started with Racket, download it from the web page and install it. If you are a beginner or would like to use a…docs.racket-lang.org

1. Getting Started With DrRacket

DrRacket is an integrated development environment (“IDE”). Much like any other IDE, we can download and launch DrRacket as an app on a computer and code in whatever language that it supports. As an example, for a Python IDE, you code in Python; however, with DrRacket, you get a variety of options that include BSL and ISL.

To actually get started, check out the docs and download the IDE from the source.

A selection of teaching languages from the DrRacket IDE. From the Author’s desktop.

A selection of teaching languages from the DrRacket IDE. From the Author’s desktop.

Why bother to explain the DrRacket IDE?

Before enrolling in a class that taught functional programming with DrRacket, I had no concept of the DrRacket IDE or its teaching languages. I was confused about where to get started. Moreover, I never heard of Racket and couldn’t find much about it on the Internet. As a result, I was frustrated about why we were about to spend so much time with the unpopular Racket kid and not learning in the popular crowd with Python and Java.

[embed]How to Design Programs htdp.org

How does learning with DrRacket work?

The DrRacket IDE is paired with a digital textbook called How to Design Programs or “HTDP” for short. In my case, I was in a class that followed parts of HTDP. The textbook has sections that explain program design concepts and presents coding challenges for students to solve with DrRacket. Early on and with high-level concepts, students reinforce their learning by coding in BSL and progress to ISL as the concepts get more involved.

After BSL and ISL, we started coding in both ISL+ and Python to practice and reinforce learning. In retrospect, coding in both languages simultaneously was beneficial in retaining, transferring, and building skills.

Why care about DrRacket at all?

Two reasons to care about DrRacket:

  • First, if you’re a student, chances are you have no choice but to figure out this Racket thing for school. As a result, the first reason to care is that you don’t have a choice.
  • Second, learning to code with DrRacket works! If you put the time in, you can transfer the skills to almost any other language. As a result, the second reason to care is that you can generally improve your software skills and have something to show for it.

[embed]Learning to Code? Learn Program Design, Too. Many jump straight into popular languages such as Java or Python. Still, program design with Racket is often overlooked…medium.com

2. Overview: BSL, ISL, ISL+, and Racket

With DrRacket, we can learn programming concepts with teaching variants of Racket. However, because of the way each variant works, it’s best to think of each as its own language. For example, as Racket variants, BSL and ISL each recognize different syntax. Likewise, the code that works for Racket might not work for BSL or ISL.

From left to right: Beginner Student Language, Intermediate Student Language, ISL with Lambdas, and Racket in the DrRacket IDE.

From left to right: Beginner Student Language, Intermediate Student Language, ISL with Lambdas, and Racket in the DrRacket IDE.

Always default to the correct source documentation.

Although we should always reference the source documentation, it is critically important to do so with DrRacket for a few reasons. Although the docs may seem cryptic at first, with some study, they quickly become your best friend. As a result, it is worth 5 minutes to read examples of syntax and learn what functions are available. Second, the message boards don’t always differentiate between BSL or ISL problems and as a result, you may end up chasing a Racket solution that is not meant for BSL.

For reference, the following are links to source documentation for each of the languages that this guide covers.

Beginning Student Language (BSL) Documentation

[embed]1 Beginning Student The grammar notation uses the notation X ... (bold dots) to indicate that X may occur an arbitrary number of times…docs.racket-lang.org

Intermediate Student Language (ISL) Documentation

[embed]3 Intermediate Student The grammar notation uses the notation X ... (bold dots) to indicate that X may occur an arbitrary number of times…docs.racket-lang.org

Intermediate Student Language with Lambdas (ISL+) Documentation

[embed]4 Intermediate Student with Lambda The grammar notation uses the notation X ... (bold dots) to indicate that X may occur an arbitrary number of times…docs.racket-lang.org

Racket Documentation

[embed]Racket Documentation This is an installation-specific listing. Running raco docs (or Racket Documentation on Windows or Mac OS) may open a…docs.racket-lang.org

Photo by ThisisEngineering RAEng on Unsplash

Photo by ThisisEngineering RAEng on Unsplash

3. Getting Started: BSL

Welcome to BSL. What to expect? Tackle foundational problems without the ease of built-in functions. The docs technically have everything you need to make magic happen, but this section contains three points I wish I knew beforehand and three examples of BSL syntax to get started.

First, a point of clarification and six top-level tips.

I call it “BSL” but really, there are two languages at the beginner level: a *Beginning Student language and then a [Beginning Student with List Abbreviations* language](https://docs.racket-lang.org/htdp-langs/beginner-abbr.html). My reference to BSL is a reference to both Beginner languages.

In BSL, six points about the syntax to be aware of.

; 1. Use "double quotes" for strings, unlike in 'Python'
; 2. Display last line of code with CMD + CTRL + Up Arrow, unlike 
; pressing Up Arrow in Terminal or in a repl
; 3. To create a variable, we usually think: a = "apple" 
; but in BSL we must use define:
(define a "apple") ; a is now a string "apple" 
(string? a) ; -> evaluates to #true
; Output > #true
#|
  4. To comment a line, use the semi-colon; or many, ;;;;; ...
or use a hash & pipe to open a comment block and pipe & hash to close a comment block
|#
#|
  5. By convention, use the dash instead of underline for variables.
Ex. a-list-of-strings in BSL instead of a_list_of_strings in Python.
|#
; 6. No commas, only spaces. For example, in BSL
; do like: (list 1 3 4 56) NOT like: (list 1, 3, 4, 56)

Second, there are two peculiar things about BSL that take a moment to get used to.

With BSL there is something called prefix notation where the operator comes before the operands. In addition, almost every operation must be surrounded by parenthesis. For example, in the code block below demonstrates the difference between prefix and infix notation with BSL.

; Example of prefix notation in BSL
#|
To add 1 and 4, we usually think infix notation: 1 + 4
however, in BSL we must think prefix notation: + 1 4
Also, to evaluate as BSL code we have to surround with (...)
|#
(+ 1 4)
; output >
; > 5
; infix notation in my brain: [(1 + 2) * (3 + 4)] / 5 - 6 = -1.8
; prefix notation in BSL: 
(- (/ (* (+ 1 2) (+ 3 4)) 5) 6)
; > -1.8
; it is normal in BSL to have a bunch of parens
; at the end of a function -> ))]))] )))))]) 
; also, yes, it is annoying

Third, BSL purposefully restricts certain built-in functions available that are otherwise available in ISL.

Part of the idea is to force students to think critically and learn how computers evaluate code. For example, how would you tell a computer to sort a list of numbers or letters without using built-in functions that sort for you?

The tempting reaction is to go with some kind of sort() function, but remember, attempts to use the sort function only return an error in BSL. As a result of the restrictions, part of the challenge in BSL is figuring out how abstract functions work. Image from the Author.

The tempting reaction is to go with some kind of sort() function, but remember, attempts to use the sort function only return an error in BSL. As a result of the restrictions, part of the challenge in BSL is figuring out how abstract functions work. Image from the Author.

Three examples of lists, structures, and functions to get going in BSL.

1. The List

The list is one of those staple dishes of any program and it should be no surprise to get a big helping in BSL. The following BSL code demonstrates a few ways to create and manage a list. Important note: a list does not have indexes for each element in BSL; however, you can still return elements in a list as shown below.

; Examples of lists in BSL
; create a list of numbers with define
; the named list is a-list-of-numbers
(define a-list-of-numbers (list 3 1 2))
; get the first number in the list
; in Python, a-list-of-numbers[0] -> 3
(first a-list-of-numbers) ; > 3
; get the second number in the list
; in Python, a-list-of-numbers[1] -> 1
(second a-list-of-numbers) ; > 1
; we can continue on with third, fourth, of a list,...
; but it is usually sufficient to call first and rest
; get the rest of the numbers in the list
; in Python, a-list-of-numbers[1:] -> [1, 2]
(rest a-list-of-numbers) ; > (list 1 2)
; Very important! Understand how first and rest work 
; to unlock recursion
; Return a bool, is the first greater than the second?
(> (first a-list-of-numbers) (second a-list-of-numbers))
; > #true
; The Sort function will not work in BSL but will work in ISL
(sort a-list-of-numbers)
; > error sort: this function is not defined

[embed]Learn and Understand Recursion in JavaScript I’ll walk you through two popular JS recursion examples in 10 minutes so you can finally understand how recursion works…codeburst.io

2. The Structure (struct) and Lists of Structures

In addition to lists, BSL has a struct data type. A close analogy is that strucs in BSL are similar to how Python implements Classes.

[embed]9. Classes - Python 3.8.5 documentation Classes provide a means of bundling data and functionality together. Creating a new class creates a new type of object…docs.python.org

The following code example demonstrates how to build a list of class-like structures in BSL.

; Example of Structure data and List of Structure data
; create a new data structure called pet
; pet params:
;  id->number, type->string, age->number, 
;  name->string, color->string, weight->number
(define-struct pet [id type age color name weight])
; create a new pet with id of 1
(define pet1 (make-pet 1 "cat" 3 "orange" "Colonel Mustard" 11.4))
(struct? pet1) ; -> evaluates to #true
(pet-name pet1) ; -> select just the name from pet1
(pet-color pet2) ; -> select just the color from pet2
; > "Colonel Mustard"
; > "black"
; create a dog and fish pet
(define pet2 (make-pet 2 "dog" 10 "black" "Sir Duke" 25))
(define pet3 (make-pet 3 "fish" 5 "gold" "Mr. Fish" .3))
; we can have a list of structures
; a-list-of-pets (alop)
; note: NO commas, only spaces
(define alop (list pet1 pet2 pet3))
alop ; -> returns the list of pets
#| the alop
> (list
>  (make-pet 1 "cat" 3 "orange" "Colonel Mustard" 11.4)
>  (make-pet 2 "dog" 10 "black" "Sir Duke" 25)
>  (make-pet 3 "fish" 5 "gold" "Mr. Fish" 0.3))
|#
pet1 ; -> returns just the one pet
; > (make-pet 1 "cat" 3 "orange" "Colonel Mustard" 11.4)

3. Functions in BSL

If you want to learn or teach recursion, BSL in DrRacket is a phenomenal tool. Like other languages, we can create functions and leverage conditional statements in BSL. However, unlike some languages, BSL really shines with recursion.

In the three code examples below, a function, get-pets, returns the name of a selected pet with conditionals and recursion.

; Example: syntax of a basic function
; given a list of pets as alop from prior example
; alop has a cat, a dog, and a fish
; 1st attempt - pass some parameters to a function
; just to see if it works
; get-pets is the function name
; we pass the alop to a-list in get-pets
(define (get-pets a-list)
  (pet-type (first a-list)))
; return the pet-type of the first pet structure
; call the get-pets function and pass it alop
(get-pets alop) ; -> "cat"

Now that we can pass something to a function, in the next example, let’s add an if-statement to compare things and see what we can do with the results.

; Example: function with an if-statement
; Given a list of pets as alop from prior example
; alop has a cat, a dog, and a fish
; 2nd attempt - add a param and see the if statement
; Test how string equality works
(define (get-pets a-list pet-property)
  ; for the first pet in the list, if the type is 
  ; equal to the second param pet-property, then

  (if (string=? (pet-type (first a-list))
                pet-property)
      "something if true"    ; replace later w/function
      "something if false")) ; replace later w/function
; if given pets and a cat, then true and do something
; if fish, then false and do something else
(get-pets alop "cat") ; -> "something if true"
(get-pets alop "fish") ; -> "something if false"

From the 2nd pass, we learn the function can take some action after comparing the pet-type property of the first pet with the pet-parameter, which is either “cat” or “fish.” Since a cat is first in the list, when we test with “cat” the if-statement is true. Along the same lines, we know that “fish” evaluates to false since the first pet is still a cat. As a result, we have two questions to answer next— what should the function do when true and what should it do when false?

; Example: a recursive function in BSL
; Given a list of pets as alop from prior example
; alop has a cat, a dog, and a fish
; 3rd attempt - add recursive steps
; return only the selected type, if matching
; get-pets() matches a-string to a pet struct
; [a-list, a-string]->[a-pet-struct]
; a-list is a list of pet structures
; a-string is one of a pet property
; pet properties: [id type age color name weight]
(define (get-pets a-list pet-property)
  ; for the first pet in the list, if the type is 
  ; equal to the second param pet-property, then

  (if (string=? (pet-type (first a-list))
                pet-property)
      (first a-list) ; if match, then return the current pet
      (get-pets (rest a-list) pet-property)))
      ; else: recursively pass the rest of the list
; if given a pets struct and cat, return the cat
; if fish, then recurse over list until a match
(get-pets alop "cat") 
  ; > (make-pet 1 "cat" 3 "orange" "Colonel Mustard" 11.4)
(get-pets alop "fish") 
  ; > (make-pet 3 "fish" 5 "gold" "Mr. Fish" 0.3)
; not covered here: what happens if we search for a snake?

BSL Summary

As the last BSL example shows above, we can learn fairly complex programming concepts such as recursion with the deceptively simple, Beginner Student language in DrRacket.

Photo by Tobias Nii Kwatei Quartey on Unsplash

Photo by Tobias Nii Kwatei Quartey on Unsplash

3. Getting Started: ISL

Welcome to Intermediate Student Language! Expect access to more functions to tackle more challenging problems.

Generally, all the concepts and syntax from BSL carry over to ISL and ISL+. The most significant change in ISL is the ability to create and leverage local functions and definitions.

When switching between languages in DrRacket, check out the bottom left menu options.

When switching between languages in DrRacket, check out the bottom left menu options.

Local Definitions in ISL

Let’s take another look at our get-pets function from earlier in the BSL section to unpack the syntax of a local function definition in ISL. In the example below, get-pets has been modified so that a variable “current-pet” is now the first pet in a list of pets each time the function runs.

; Example of a local function definition
; same get-pets function as before but with a local def
(define (get-pets a-list pet-property)
  (local
    ; locally define the first of a list as current-pet
    ; instead of calling (first a-list) multiple times
    ((define current-pet (first a-list)))
; after local defs, do some operation
    (if (string=? (pet-type current-pet)
                  pet-property)
        current-pet
        (get-pets (rest a-list) pet-property))))
(get-pets alop "fish") 
  ; > (make-pet 3 "fish" 5 "gold" "Mr. Fish" 0.3)
  ; same result as before

Now that we have a basic grasp of local definitions in ISL, let’s go a little further with a few working ISL examples of conditional statements (cond), list construction (cons), and append.

Local Function Definitions in ISL

In the preceding example, we defined a variably locally but it was kind of trivial — the local definition only simplified code but did not do much else. What if we want to filter the list of pets by weight in a slightly more complicated problem?

We could start by building a recursive function that compares each item in the list to a number. If pet-weight is less than a given number, we add the pet to a list; if greater than the number, we skip it until we have a list of pets that meet the weight restriction. Lastly, return just a list of pets that are less than the weight limit.

In the first example below, we start building a function, filter-pet-weight.v1. Notice how we can use a cond statement instead of the if-statement from the previous examples.

; A first pass at recursive filtering
; Caution! This results in a stack overflow, can you see why?
(define (filter-pet-weight.v1 a-list a-weight)
  ; a conditional set of statements instead of if-statement
  (cond
    ; if first pet-weight is less than, then, do recursive call
    ; pass the list and the weight to compare

    [(> (pet-weight (first a-list)) a-weight) (filter-pet-weight.v1 a-list a-weight)]
    ; if not, construct (cons) the first item 
    ; in the list and recursively call
    [else (cons (first a-list) (filter-pet-weight.v1 (rest a-list) a-weight))]))
(filter-pet-weight.v1 alop 12) 
; > results in a stack overflow, keeps running until out of memory

In the first pass above, we run into a stack overflow — the function keeps making a recursive call to no end. Eventually, the program will run out of memory and fail because our logic has no exit condition.

[embed]Recursion is not hard: a step-by-step walkthrough of this useful programming technique I’m going to say this right off the bat. Do you know the events that happen upon function invocation? No? Then that’s…medium.com

When faced with stack overflow, think about how the function should keep going and what conditions should stop it. The conditions that stop the function are usually known as a base case and the conditions that keep it running are recursive cases. If working with a list, typically, evaluate the list until it is empty. The trick is to make sure we increment the list with each pass.

In version 2 below, we have a recursive solution with a good base case and recursive case that works the whole list and avoids stack overflow.

; A second pass at recursive filtering
; This time we can exit recursion
(define (filter-pet-weight.v2 a-list a-weight)
  (cond

    ; base case -> when empty list, return an empty list

    [(empty? a-list) '()]
; here we pass the rest of the list in a recursive call
; to ensure we incrementally move towards the end of a list

    [(> (pet-weight (first a-list)) a-weight) (filter-pet-weight.v2 (rest a-list) a-weight)]
; instead of cons, we append a list of items 
; in each recursive call
    [else (append (list (first a-list)) (filter-pet-weight.v2 (rest a-list) a-weight))]))
(filter-pet-weight.v2 alop 12)
; > (list
     (make-pet 1 "cat" 3 "orange" "Colonel Mustard" 11.4)
     (make-pet 3 "fish" 5 "gold" "Mr. Fish" 0.3))

In the example above, the filter function returns all pets under 12. Also, notice how we can generally use append interchangeably with cons as long as we append a list of things.

Lastly, since we are in ISL and have access to more stuff, lets see about using a built-in filter function. In version 3 below, notice how we define a local function that compares pet-weight to a given number. Then, with the built-in filter function, we can create a new list (from filtering our pet-weight function) to a list of pets.

; A third pass at recursive filtering
; Using a built-in function to simplify code
(define (filter-pet-weight.v3 a-list a-weight)
  (local
     ; a local function def called weight-limit
     ; consumes a pet structure
    ((define (weight-limit a-pet)
       ; compares the pet-weight against a-weight
       (< (pet-weight a-pet) a-weight)))

    ; filter is a built-in function in ISL
    ; applies a function to a alist
    (filter weight-limit a-list)))
(filter-pet-weight.v3 alop 12)
; same result as in version 2 but simpler code
; > (list
     (make-pet 1 "cat" 3 "orange" "Colonel Mustard" 11.4)
     (make-pet 3 "fish" 5 "gold" "Mr. Fish" 0.3))

ISL Summary

With ISL, we get everything from BSL plus the ability to define local variables and functions. In addition, we get access to built-in functions such as sort and filter. There is much more to ISL, but hopefully, the examples provided in this section got you started in the right direction.

Photo by Sam Carter on Unsplash

Photo by Sam Carter on Unsplash

5. Getting Started: ISL with Lambdas

Welcome to ISL+! The official name is *Intermediate Student with Lambda, but ISL+* is what my professor called it, it’s catchy and makes sense. Just like the transition from BSL to ISL, we get everything from before with ISL+. What’s new? As the name suggests, in ISL+ we get the ability to build lambda functions.

What to Pay Attention to in ISL+

Very useful: map, **foldl/foldr, and [tests](https://docs.racket-lang.org/htdp-langs/intermediate-lam.html#%28form._%28%28lib._lang%2Fhtdp-intermediate-lambda..rkt%29._check-expect%29%29) (check-expect). These three sets of functions are available in either BSL or ISL; however, to demonstrate how they work, it is useful to introduce them with lambdas. For example code, let’s re-visit the get-pets **function from BSL and see how we can solve the filter problem in ISL+.

The Filter Problem (recap)

We have a list of pets contained in a list of structs. Each pet struct has properties such as name, weight, and type. Main function: if given a list of pets and a pet-type, for example, “cat”, we want to return only cats. The code for this pet data is the same as it is for BSL as shown below.

; Example code for creating a struct and a list of structs
(define-struct pet [id type age color name weight])

(define pet1 (make-pet 1 "cat" 3 "orange" "Colonel Mustard" 11.4))
(define pet2 (make-pet 2 "dog" 10 "black" "Sir Duke" 25))
(define pet3 (make-pet 3 "fish" 5 "gold" "Mr. Fish" .3))

A Filter Solution Without Lambdas (recap)

Before going into the lambda solution, let’s take a quick look at how we solved this filter problem before in BSL. The if-statement, in the code below (bold text), basically did the filtering job. After filtering, we used recursion to cycle through the list of pets.

What if we don’t want to use recursion? How else can we solve this problem?

; Example code for filtering a list of structs
(define (get-pets a-list pet-property)
  (local
    ((define current-pet (first a-list)))
    (if (string=? (pet-type current-pet)
                  pet-property)
        current-pet
        (get-pets (rest a-list) pet-property))))

Filter With Lambdas

I thought lambdas were intimidating to work with at first glance; however, after peeling back the “mathy” name, they aren’t so bad. What’s a lambda? Basically, lambda is a function that behaves like a function — it takes variables and evaluates an expression — except, we don’t give it a name. Instead of a named function, we just use the term lambda.

Since the whole bit about unnamed functions can seem abstract at first, let’s walk through the code, starting with a different way to check equality. Before, we used string=?, but as shown below we can also use equal? which returns a boolean value (true or false).

; Example of using equal?
(define (get-pets a-list pet-property)
  ; before we used string=? 
  (equal? (pet-type (first a-list)) pet-property))
; > #true

At this point, we have the ability to check equality for one record in a list of records. Previously we used recursion, but now we can use lambdas and some other helpful built-in functions to get there a little bit quicker.

To wrap my head around lambdas, I like to first think about what I want to iterate or recurse through. In this case, we have a list of pets and want to visit each pet’s type.

; Example of a first pass at lambda construction
(define (get-pets a-list pet-property)
  ; replace the following line
  ; (equal? (pet-type (first a-list)) pet-property))  
  ; build a lambda instead
  (lambda (x) (equal? (pet-type x) pet-property)))
#| Explanation: 
Before, we looked at the pet-type of the first item in a-list. In the lambda, replace (first a-list) with x. As a result, the function reads like: lambda consumes x which will be an item in a-list, then, check equality between pet-type of x and the given pet-property.
|# 
; > (lambda (a1) ...)

At this point, our little lambda is still, just a lambda. That is, we haven’t specified what x should be. As a result, we only have an output that looks like (lambda (a1) …). Do not be dismayed, in fact, this is the coolest part!

Our lambda is cool because it checks equality for a data type that we created from scratch. By comparison, if we wanted to filter a list of numbers for parity, we might use the built-in function odd? — that’s what our lambda is doing for pet structures. For example, the documentation describes how to use filter by showing how to filter with odd? on a list of numbers.

; Example of filter from the docs
(filter odd? '(0 1 2 3 4 5 6 7 8 9))
; > (list 1 3 5 7 9)
; instead of odd? we have a lambda on a different list
(filter (lambda (x) (equal? (pet-type x) pet-property)) a-list)

Finally, let’s put all the pieces together under one function in the code below.

; Example of a second pass at lambda construction
(define (get-pets a-list pet-property)
  (filter (lambda (x) (equal? (pet-type x) pet-property)) a-list))
#| Explanation: 
We use filter to apply our lambda to a-list, one x at a time. 
|#
(get-pets alop "cat")
; > (list (make-pet 1 "cat" 3 "orange" "Colonel Mustard" 11.4))

Test, Test, Test

Although we should have been testing all along, now that we have a working lambda, let’s take a look at how to build tests with a focus on two types: check-expect and check-within. Along the way, we can also wrap up some code examples with map and foldl/foldr.

First, the check-expect test. By convention, you want test coverage on all parts of each function. DrRacket will even let you know whether “all expressions are covered” by a test. Since we know a “cat” should return a cat struct, let’s build a test for cats.

; Example of a testing with check-expect
; call the function from check-expect
(check-expect (get-pets alop "cat") 
              ; the second argument is the expected result
              (list 
              (make-pet 1 "cat" 3 "orange" "Colonel Mustard" 11.4)))
; the function to be tested
(define (get-pets a-list pet-property)
  (filter (lambda (x) (equal? (pet-type x) pet-property)) a-list))
; > The test passed!

Second, there’s the check-within test. Testing with check-within is helpful when working with mathy operations. For instance, if the expected result of some function is pi and I get 3.14, the function probably works just fine. However, a check-expect test fails in the pi scenario because the answer is not exactly pi, its just close to pi. As a result, we want to check-within a range to test.

To demonstrate check-within, map, and foldl/foldr, lets see about calculating the average weight of all our pets.

; Example of map and lambda (version 1)
; the lambda simply returns the pet-weight of each x
; map applies the lambda to a-list;
; as a result, x cycles through the list
(define (ave-weight.v1 a-list)
  (map (lambda (x) (pet-weight x)) a-list))
; > (list 11.4 25 0.3)

To return the average pet weight, we need a sum of pet weights and a count of pets. In version 1 above, we use map to apply a function (the lambda) to a list and we return a new list of only weights — from here we can get the sum, count, and finally, their average.

[embed]Unlock Basic Lambdas in Python For anyone — get over the top to conquer lambdas in Python with a working example of lambdas, map, and reduce on real…medium.com

To get the sum of a list, we can use foldl or foldr — short for fold left and fold right. If an analogy helps, fold in ISL is like using reduce in Python, i.e. reduce a list of numbers to their sum. The difference with fold is that we can choose a direction to process the list. In the case of sum, the direction does not matter so we will just use foldr in this guide.

; Example of using foldr
; foldr usage
; function | a base value | a list
;  add (+)        0         (list 11.4...)
(foldr + 0 (list 11.4 25 0.3))
#| Explanation: 
For each item in the list, apply the function (add) and start at 0. As a result, we might have 0 + 0.3, then, 0.3 + 25, and then 25.3 + 11.4 to get 36.7.
|#
; > 36.7

In a final version of ave-weight, as shown below, we can define values such as the list of numbers (alon), their sum, and a count of alon as variables, then do some simple division with prefix notation to return the average.

; Example of putting it all together in ave-weight
(define (ave-weight a-list)
  (local
   ; create a list of numbers (alon) with map
    ((define alon (map (lambda (x) (pet-weight x)) a-list))    

   ; foldr to get a sum 
     (define alon-sum (foldr + 0 alon))   

   ; count length                  
     (define alon-count (length alon)))  
   ; divide and return average                 
    (/ alon-sum alon-count)))
(ave-weight alop)
; > 12.23
; This function works, but it's still not the most optimal...
; Not covered here: how to make this solution more efficient?

Lastly, since the average is actually 12.23 repeating, we can use check-within to create a working test for ave-weight. As shown below, the test passes if the result is within 1/100th of 12.23.

; Example of check-within 
; the function to test | expected result | expected delta
; test passes if result is within the delta
(check-within (ave-weight alop) 12.23 .01)

ISL+ Summary

With ISL+, we carried everything over from BSL and ISL but got the added bonus of lambda functions. In addition, working with lambdas helps explain how to use other interesting functions such as filter, map, and fold. We also have some examples of how to test with check-expect and check-within. Just like in ISL, there are a ton more problems and functions to cover, but hopefully, this is enough to get started in ISL+!

Photo by Christian Tenguan on Unsplash

Photo by Christian Tenguan on Unsplash

6. Getting Started: Racket

Although opinions may vary, learning to design programs in BSL and ISL is helpful because it is easy to stay focused on the core concepts. For example, students can focus on understanding as recursion and lambdas and not deal with importing packages and and setting up libraries. However, Racket is the professional language that requires “all the other stuff” — the fundamental concepts carry over, but syntax changes drastically. For example, you have to specify language with #lang and import libraries and packages for functionality.

What to Watch Out For in Racket

Racket is not BSL or ISL! In all honesty, I barely know enough to scratch the surface on Racket. I only know what I know because I had no idea what I was looking at when I first got started in BSL.

While working in a BSL module, ended up spending a few hours reading documentation about Racket and was obviously confused. Frustrated, I thought, why didn’t we go over all this stuff in class?! As a result, the big point here is that if you’re learning in BSL or ISL and run into strange error messages like “unbound identifier,” you might need to switch out of Racket. However, if curious on how to make a few small things work in Racket, read on about writing a test and contract below.

What’s a Contract?

Until this point, I used comments to specify what type of data each function expects — the comments are meant to express a contract between the function and user. However, in Racket, we finally have the ability to write the contract out in code and enforce it. For example, before, I expected the pet-name to be a string but without a real contract, the user can create a pet-name with any value. If the function relies on a string value but gets a number, there could be big problems.

The DrRacket IDE with Racket contracts and check tests.

The DrRacket IDE with Racket contracts and check tests.

To demonstrate how contracts work in Racket, let’s take a look at calculating the area of a circle. Begin with specifying language and importing packages.

; first, specify language and import necessary packages
#lang racket
(require racket/contract)
(require rackunit)

As for logic, since the area of circle is given by pi(r²), when r=1 the area is ~3.14 and when r = 2 the area = ~12.56. In the code below, if we continue in Racket, we can use check-= instead of check-within to test our circle-area function.

; simple function to calc circle area
(define (circle-area r)
  (* pi (sqr r)))
; tests run silent,
; if tests pass; nothing
; if error; error messages
; check-= instead of check-within
(check-= (circle-area 1) 3.141 0.001)
(check-= (circle-area 2) 12.566 0.001)

While we use check-= above to ensure our function works as expected, we can write a contract to ensure the function gets appropriate data. Before in BSL and ISL, we might write comments to explain the contract as shown below.

; Example of a contract in BSL or ISL
; circle-area()
; [number] -> number
(define (circle-area r)
  (* pi (sqr r)))

In Racket, we can enforce the contract to make sure that circle-area only consumes a number value.

; Example of a contract in Racket
(provide (contract-out
          [circle-area (-> number? any)]))
(define (circle-area r)
  (* pi (sqr r)))
; if we provide a string as "3", then contract violation
(circle-area "3")
; > sqr: contract violation
; > expected: number?
; > given: "3"

For context on the IDE, the code above is illustrated in the following screen shot of DrRacket. Here, you can see the linkage from contract to function and tests highlighted with arrows and circles.

The DrRacket IDE with the result of an error message from a contract.

The DrRacket IDE with the result of an error message from a contract.

Racket Summary

Racket is a professional programming language that goes beyond the teaching scope of BSL and ISL. However, since DrRacket provides the ability to code in multiple languages, students sometimes confuse Racket for BSL. I was one such student that spent hours in a state of confusion when I stumbled into Racket; however, I fortunately learned a few things along the way. I hope this bit on getting started with Racket was helpful to both steer you back to BSL or ISL but also explore a little curiosity on the main Racket language.

Photo by Kelly Sikkema on Unsplash

Photo by Kelly Sikkema on Unsplash

Conclusion

I learned a ton about programming with DrRacket and made plenty of mistakes along the way. However, I went through some unnecessary pain because I did not have a good idea of what life would be like in BSL and ISL.

In this story, I share excerpts from my notes as a student working in BSL, ISL, and ISL+ in hopes that other students can tackle DrRacket with a running start.

When combined with HTDP, DrRacket is a great tool to learn about foundational programming and concepts such as recursion and lambdas. Further, students should be aware that BSL and ISL are different than Racket which is a full-on professional programming language.

Although it can be tempting to look at BSL or ISL with disdain, beginner students stand to learn a great deal about designing programs and building skills in software development.

If I can improve this guide or answer any questions about learning with BSL or ISL, please drop a comment! Thanks!

For Further Reading, You Might be Interested In

[embed]Beautiful Racket by Matthew Butterick an intro­duc­tion to language-oriented program­ming using Racket by Matthew Butt­erick · version 1.6beautifulracket.com

[embed]Learn the fundamentals of functional programming If you’re a software developer, you’ve probably noticed a growing trend: software applications keep getting more…medium.com

[embed]The Rise and Fall and Rise of Functional Programming (Composing Software) Note: This is part of the “Composing Software” series (now a book!) on learning functional programming and…medium.com

[embed]Advent of Haskell Thoughts and lessons learned after using Haskell consistently for 25 days in a rowmedium.com


메타데이터
post_id
bcac4153710e
slug
a-guide-to-programming-with-drracket-bcac4153710e
url
https://levelup.gitconnected.com/a-guide-to-programming-with-drracket-bcac4153710e
canonical_url
https://levelup.gitconnected.com/a-guide-to-programming-with-drracket-bcac4153710e
author_url
https://medium.com/@justinhchae
status
ok
fetched_at
2026-07-13 23:08:50