Add simplify-named-let-initialization refactoring rule
Adds a refactoring rule to extract complex multi-line initialization expressions from named let loops into separate define bindings, improving readability by reducing nesting.
Implementation
-
Rule: Extracts multi-line named
letinitializers intodefinebindings prefixed withinit- - Safety: Only applies when all non-extracted expressions are pure (prevents reordering side effects)
- Scope: Definition contexts only (e.g., function bodies)
Example
;; Before
(define (f a b c)
(let loop ([x (+ 1 2 3)]
[y (if (a)
(b)
(c))])
(loop x y)))
;; After
(define (f a b c)
(define init-y
(if (a)
(b)
(c)))
(let loop ([x (+ 1 2 3)]
[y init-y])
(loop x y)))
Files
-
default-recommendations/simplify-named-let-initialization.rkt- Rule implementation -
default-recommendations/simplify-named-let-initialization-test.rkt- Test suite covering positive and negative cases -
default-recommendations.rkt- Added to default suite
Addresses #756
Original prompt
This section details on the original issue you should resolve
<issue_title>New rule:
simplify-named-let-initialization</issue_title> <issue_description>### Rule summaryOld scheme code that doesn't use internal definitions sometimes uses complex named
letloops with very large expressions used as the initial values for the namedlet's arguments. Since all of a namedlet's initial arguments are evaluated before anything in the namedletis, it's more readable to extract complex initial value expressions into definitions preceding the namedlet.This should only be done for named
letargument initialization expressions that take up multiple lines, and it's only safe when the other initialization expressions are pure. It's also only possible in a definition context. The variable name chosen for the initial value should be the corresponding namedletargument variable name prefixed withinit-.This rule should not be included alongside the
let-replacementrules, since it isn't actually replacing aletbinding with adefine: it's introducing a newdefinebinding to make an existingletbinding easier to read.Test case
#lang resyntax/test test: "original code should be refactorable to new code" -------------------- #lang racket (define (f a b c) (let loop ([x (+ 1 2 3)] [y (if (a) (b) (c))]) (loop x y))) ==================== #lang racket (define (f a b c) (define init-y (if (a) (b) (c))) (let loop ([x (+ 1 2 3)] [y init-y)]) (loop x y))) --------------------No-change test case
#lang resyntax/test no-change-test: "code not refactorable" -------------------- #lang racket (define (f a b c) (let loop ([x (displayln "foo")] [y (if (a) (b) (c))]) (loop x y))) --------------------Additional context
Saw this in racket/scribble#535, specifically this commit.</issue_description>
Comments on the Issue (you are @copilot in this section)
- Fixes jackfirth/resyntax#745
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.