Hello! I am using Racket in a course at my university and I've found it quite enjoyable.

I am working on an assignment where the use of `let`

and `let*`

is limited so as to encourage good functional style. While I understand the motivation, this sent me down a rabbit hole of trying to find a way to write my code using `let`

and `let*`

and writing a program to automatically expand it to use `lambda`

instead.

I posted my question on StackOverflow (linked below), but I'll include the contents here as well.

Any advice would be much appreciated (:

## Context

This question is tangentially related to a homework assignment, but I am not looking to have someone do my work for me.I have an assignment where we are discouraged from overuse of

`let`

,`let*`

, and similar functions to encourage us to "think functionally".I also understand that expressions using

`let`

can be expanded to only use`lambda`

in the following manner.`#lang racket ; using let (let ([x 1] [y 2]) (+ x y)) ; with lambda ((lambda (x y) (+ x y)) 1 2)`

Looking at this caused me to ask the following question.

## The Question

Is there some way to accomplish this expansion programmatically? The only part that should be modified are those using

`let`

,`let*`

, and so on.`> (expand-let '(let ([x 1] [y 2]) (+ x y))) '((lambda (x y) (+ x y)) 1 2)`

It would be cool if this could apply to all "local binding" Racket functions:

`let`

,`let*`

,`letrec`

,`let-values`

,`let*-values`

,`letrec-values`

, and so on.

As pointed out in the comments, this doesn't really serve any practical purpose for my assignment, but I am still looking into it as a personal project as I am new to Racket. Any advice is appreciated.## What I've tried

- Sylwester's answer to Not able to use let in Racket discusses the expansion of
`let`

and`letrec`

but does so by hand.- Alex Knauth's answer to When to use define and when to use let in racket includes a helpful discussion of the scopes of
`let`

,`let*`

, and`letrec`

.- Partial expansion of code in scheme is honestly pretty close to what I am looking for, but I will point out a few differences

I don't need to evaluate the code, simply expand what I consider to be "syntactic sugar" in this context

The question requires

`#lang scheme`

, but this question uses`#lang racket`

A comment refers the OP to Racket's Sandboxed Evaluation but I am not sure that is what I am looking for (advice is appreciated). It seems like this is used to provide some control over how code is evaluated, but what I am looking for is not control over evaluation. It is about expanding functions used for local bindings when they can be rewritten using

`lambda`

.The answer links to

`expand`

and this seems to get me closer (maybe?) but I am struggling to find a way forward.`> (syntax-e (expand '(let ([x 1] [y 2]) (+ x y)))) '(#<syntax:/Applications/Racket v8.11.1/collects/racket/private/qq-and-or.rkt:193:51 let-values> #<syntax (((x) (quote 1)) ((y) (quote 2)))> #<syntax:/Applications/Racket v8.11.1/collects/racket/private/kw.rkt:1263:25 (#%app + x y)>)`

## Coding it myself (not a complete solution)

I attempted my own solution as a beginner and got stuck. Any advice is appreciated.

`(define (expand-let atomized-expr) (expand-let-helper (syntax->datum (expand atomized-expr)) (lambda (result) result))) (define (expand-let-helper parse-tree return) (define (expand-let-bindings bindings) (map (lambda (binding) (list (caar binding) (expand-let (cadr binding)))) bindings)) (cond [(null? parse-tree) (return '())] [(not (pair? parse-tree)) (return parse-tree)] [(eq? (car parse-tree) '#%app) (expand-let-helper (cdr parse-tree) (lambda (result) (return result)))] [(eq? (car parse-tree) '#%top) (return (cdr parse-tree))] [(eq? (car parse-tree) 'let-values) (let* ([bindings (cadr parse-tree)] [body (cddr parse-tree)] [expanded-bindings (expand-let-bindings bindings)] [vars (map car expanded-bindings)] [vals (map cadr expanded-bindings)]) (expand-let-helper body (lambda (result) (return `((lambda (,@vars) ,@result) ,@vals)))))] [else (expand-let-helper (cdr parse-tree) (lambda (cdr-result) (expand-let-helper (car parse-tree) (lambda (car-result) (return (cons car-result cdr-result))))))]))`

See results here:

`; successful case > ((lambda (x y) (let ((a (+ x y)) (b (- x y))) (* a b))) 1 2) -3 > (expand-let '((lambda (x y) (let ((a (+ x y)) (b (- x y))) (* a b))) 1 2)) '((lambda (x y) ((lambda (a b) (* a b)) (+ x y) (- x y))) '1 '2) > (eval (expand-let '((lambda (x y) (let ((a (+ x y)) (b (- x y))) (* a b))) 1 2))) -3 ; failing cases (pointed out in comments) > (expand-let '(let ([n (random -10 10)]) (cond [(positive? n) 1] [(negative? n) -1] [else 0]))) '((lambda (n) (if (positive? n) '1 (if (negative? n) '-1 '0))) (random '-10 '10))`

*Edit #1: Edited to add more links; I was originally restricted as a new user.*

*Edit #2: Updated the quoted text from the StackOverflow question.*