# You can't use local in BSL. :-(

**URL:** https://racket.discourse.group/t/you-cant-use-local-in-bsl/4127
**Category:** Questions & Answers
**Tags:** htdp
**Created:** [February 27, 2026, 5:00am UTC](https://racket.discourse.group/t/you-cant-use-local-in-bsl/4127 "2026-02-27T05:00:32Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![ToddOBryan](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/toddobryan/32/1962_2.png) [@ToddOBryan](https://racket.discourse.group/u/ToddOBryan)
#### Post date: [February 27, 2026, 5:00am UTC](https://racket.discourse.group/t/you-cant-use-local-in-bsl/4127/1 "2026-02-27T05:00:32Z")

</div>

I don't know if this is a feature request or just a whine, but I would love to be able to introduce `local` before list abbreviations.

Unfortunately, you can't use `local` in BSL, because the macro raises an error if you have a `define` that's not on the top level. I can use `let`, but `local` is especially nice, because it doesn't involve new syntax. Students just keep using the `define` they used at the top level, but hide the scope inside a function (or an expression or whatever). In particular, it's nice that `local` lets them use `define` to name constants _or_ functions, whereas `let` would require the introduction of `lambda` for that.

I looked at the code to see if I could easily fix the problem, but the code is quite dense.

I similarly was trying to figure out if I could modify `define-struct` in two ways:

1. I wanted students to be able to put signatures for fields: `(define-struct student ([first String] [last String] [grade Natural])` and have signatures provided for `make-student` and the selectors.

2. I wanted to auto-create something like lenses for structs. Especially when you're trying to create worlds, you spend a lot of time copying state. A handler may want to modify one or two fields in a struct that has several, so you `(make-struct (struct-field1 orig-struct) (struct-field2 orig-struct) ... (f (struct-fieldn orig-struct)))` and you forget what you were trying to do, or your code gets long and you can't see all of it at once.

What I'm envisioning is that a struct like `posn` would get `posn-with-x` and `posn-with-y`, that would take a `posn` and a new `x` or `y` value and handle all the boilerplate.

In other words, to promote a student `stu` to the next grade, you'd just do `(student-with-grade stu (+ 1 (student-grade stu)))`.

Unfortunately, I think I found out where `define-struct` is defined, but there's a ton of stuff in there, and I can't find the place where the constructors, selectors, and predicate are actually generated. I guess it's possible because they're actually generated by something like `define-record-type`, but I kind of got lost in the details.

---

<div class="post-metadata">

### Author: ![shhyou](https://avatars.discourse-cdn.com/v4/letter/s/ccd318/32.png) [@shhyou](https://racket.discourse.group/u/shhyou)
#### Post date: [February 27, 2026, 5:40am UTC](https://racket.discourse.group/t/you-cant-use-local-in-bsl/4127/2 "2026-02-27T05:40:39Z")

</div>

> [@ToddOBryan](#):
>
> I don't know if this is a feature request or just a whine, but I would love to be able to introduce `local` before list abbreviations.

Perhaps a workaround is to base the language on ISL, then set the parameter [`abbreviate-cons-as-list`](https://docs.racket-lang.org/pconvert/index.html#%28def._%28%28lib._mzlib%2Fpconvert..rkt%29._abbreviate-cons-as-list%29%29) to `#false`. For example, this can be done by providing a module with `(abbreviate-cons-as-list #f)` and asking the students to `require` it.

The effect of setting this parameter to `#false` is this:

```scheme
#lang htdp/isl

(cons "fst" (cons "snd" empty))
;; => (list "fst" "snd")

(require mzlib/pconvert)
(abbreviate-cons-as-list #false)

(cons "fst" (cons "snd" empty))
;; => (cons "fst" (cons "snd" '()))

```

---

<div class="post-metadata">

### Author: ![EmEf](https://avatars.discourse-cdn.com/v4/letter/e/53a042/32.png) [@EmEf](https://racket.discourse.group/u/EmEf)
#### Post date: [February 27, 2026, 10:20pm UTC](https://racket.discourse.group/t/you-cant-use-local-in-bsl/4127/3 "2026-02-27T22:20:00Z")

</div>

You have two requests, of which I consider the second one quite reasonable in the abstract because I have had it on my mind for a decade 😑

Concretely:

1. Shriram and I have argued about naming values (local defines) in BSL or BSL+ time and again. He also firmly embraces the idea (though in detail, his request differs from yours). Why do I disagree? -- I consider BSL as a vehicle to teach both algebraic ideas and programming ideas (and in my mind they coincide). The goal is to have students break tasks into smaller functions, one per task, and to learn that function composition works.  
For reasons I don't get, students at this stage are always tempted to write very large functions that do everything in one fell swoop instead of thinking about the pieces of a task. -- More concisely: show me a place where `local` is desirable, and I show you a way to leverage basic pre-algebra knowledge.

2. While I consider a full lens library per se as overkill, I agree that the overhead of changing something inside of a struct tree is too complex. But, just like for (1), I think bringing across that composition works might be a better experience here.

If your students are taking programming in high school to become software engineers, the above paragraphs point in the wrong direction. If they take it to learn how to solve problems in a visceral manner (unlike the normal paper-and-pencil math), my explanations are on target (I think).

---

<div class="post-metadata">

### Author: ![maueroats](https://avatars.discourse-cdn.com/v4/letter/m/c0e974/32.png) [@maueroats](https://racket.discourse.group/u/maueroats)
#### Post date: [March 2, 2026, 9:36pm UTC](https://racket.discourse.group/t/you-cant-use-local-in-bsl/4127/4 "2026-03-02T21:36:08Z")

</div>

Have you considered using the [`struct++`](https://docs.racket-lang.org/struct-plus-plus/index.html) code for your desired updater functions?

Or just make "write the updater functions after you write the `define-struct`" into your design recipe? I have tried that, with moderate success. There's a lot of beginners that need to write out every detail, though.

Specifying a few required helper functions like `update-ball-velocity : ball -> ball` is another way to force students to break up the monolithic updater functions.

I assume you have thought about both of those and are just playing with the language part.

---

<div class="post-metadata">

### Author: ![ToddOBryan](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/toddobryan/32/1962_2.png) [@ToddOBryan](https://racket.discourse.group/u/ToddOBryan)
#### Post date: [March 3, 2026, 1:27pm UTC](https://racket.discourse.group/t/you-cant-use-local-in-bsl/4127/5 "2026-03-03T13:27:01Z")

</div>

I was unaware of `struct++`, which looks really cool. I'll take a look.

---

<div class="post-metadata">

### Author: ![ToddOBryan](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/toddobryan/32/1962_2.png) [@ToddOBryan](https://racket.discourse.group/u/ToddOBryan)
#### Post date: [March 3, 2026, 1:32pm UTC](https://racket.discourse.group/t/you-cant-use-local-in-bsl/4127/6 "2026-03-03T13:32:50Z")

</div>

I want to take the time to respond to this thoughtfully, but I'm in the middle of a grading/planning blitz at the moment.

Until I have time to do that, I want to say that I think of the main use of a `local/let` is abstraction and DRY. Instead of having something like `(find-next-x (posn-x (world-loc world)))` in a couple of locations in a function (say, checking whether I've hit an edge, and then again when I actually need to return the new posn), I can just do `(local [(define next-x ...)] ...)` and I can stop thinking about how I got the value.

Let me be clear, I _hate_ long functions as well, and I do not want to allow students to use `local/let` to write long functions. But being able to name things allows students to work at a slightly higher, more abstract, level, and lets them think about what they're trying to do with bigger chunks.
