Skip to content

Commit 98962bc

Browse files
Copilotjackfirth
andcommitted
WIP: fuse-map-with-for rule implementation in progress
Co-authored-by: jackfirth <[email protected]>
1 parent 3c547a0 commit 98962bc

File tree

2 files changed

+193
-0
lines changed

2 files changed

+193
-0
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
#lang resyntax/test
2+
3+
4+
require: resyntax/default-recommendations/loops/fuse-map-with-for fuse-map-with-for
5+
6+
7+
header:
8+
- #lang racket/base
9+
10+
11+
test: "map producing list for for* loop can be fused"
12+
--------------------
13+
(define (f xs g h)
14+
(define ys (map (λ (x) (g x)) xs))
15+
(for* ([y (in-list ys)]
16+
[z (in-list (h y))])
17+
(displayln z)))
18+
====================
19+
(define (f xs g h)
20+
(for* ([x (in-list xs)]
21+
[y (in-list (g x))]
22+
[z (in-list (h y))])
23+
(displayln z)))
24+
--------------------
25+
26+
27+
test: "map producing list for for loop can be fused"
28+
--------------------
29+
(define (f xs g)
30+
(define ys (map (λ (x) (g x)) xs))
31+
(for ([y (in-list ys)])
32+
(displayln y)))
33+
====================
34+
(define (f xs g)
35+
(for ([x (in-list xs)])
36+
(define y (g x))
37+
(displayln y)))
38+
--------------------
39+
40+
41+
no-change-test: "map with short lambda but ys used elsewhere not refactorable"
42+
--------------------
43+
(define (f xs g h)
44+
(define ys (map (λ (x) (g x)) xs))
45+
(for* ([y (in-list ys)]
46+
[z (in-list (h y))])
47+
(displayln z))
48+
(displayln ys))
49+
--------------------
50+
51+
52+
no-change-test: "map with non-short lambda not refactorable"
53+
--------------------
54+
(define (f xs)
55+
(define ys (map (λ (x) (+ x 1)) xs))
56+
(for ([y (in-list ys)])
57+
(displayln y)))
58+
--------------------
59+
60+
61+
test: "map with lambda that has multiple body forms is refactorable"
62+
--------------------
63+
(define (f xs g)
64+
(define ys (map (λ (x) (displayln x) (g x)) xs))
65+
(for ([y (in-list ys)])
66+
(displayln y)))
67+
====================
68+
(define (f xs g)
69+
(for ([x (in-list xs)])
70+
(displayln x)
71+
(define y (g x))
72+
(displayln y)))
73+
--------------------
74+
75+
76+
test: "map with long single-body lambda is refactorable"
77+
--------------------
78+
(define (f xs)
79+
(define long-name 42)
80+
(define ys
81+
(map (λ (x)
82+
(+ x long-name))
83+
xs))
84+
(for ([y (in-list ys)])
85+
(displayln y)))
86+
====================
87+
(define (f xs)
88+
(define long-name 42)
89+
(for ([x (in-list xs)])
90+
(define y (+ x long-name))
91+
(displayln y)))
92+
--------------------
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#lang racket/base
2+
3+
4+
(require racket/contract/base)
5+
6+
7+
(provide
8+
(contract-out
9+
[fuse-map-with-for refactoring-suite?]))
10+
11+
12+
(require resyntax/base
13+
resyntax/default-recommendations/analyzers/identifier-usage
14+
resyntax/default-recommendations/let-replacement/private/let-binding
15+
resyntax/default-recommendations/private/lambda-by-any-name
16+
syntax/parse)
17+
18+
19+
;@----------------------------------------------------------------------------------------------------
20+
21+
22+
;; A short lambda suitable for fusing with a for loop. Needs both single-attribute and
23+
;; multi-attribute versions of the body for different template contexts.
24+
(define-syntax-class fuseable-map-lambda
25+
#:attributes (x single-body [multi-body 1])
26+
27+
;; Lambdas with let expressions that can be refactored - must come first as it's most specific
28+
(pattern
29+
(_:lambda-by-any-name (x:id)
30+
original-body:body-with-refactorable-let-expression)
31+
#:attr [multi-body 1] (attribute original-body.refactored)
32+
;; For single body, we need to wrap multiple forms in a begin
33+
#:attr single-body #'(begin original-body.refactored ...))
34+
35+
;; Lambdas with multiple body forms (two or more)
36+
(pattern (_:lambda-by-any-name (x:id) first-form second-form rest-form ...)
37+
#:attr [multi-body 1] (cons (attribute first-form)
38+
(cons (attribute second-form) (attribute rest-form)))
39+
#:attr single-body #'(begin first-form second-form rest-form ...))
40+
41+
;; Short lambdas with a single body form
42+
(pattern (_:lambda-by-any-name (x:id) only-form)
43+
#:attr [multi-body 1] (list (attribute only-form))
44+
#:attr single-body #'only-form))
45+
46+
47+
(define-definition-context-refactoring-rule fuse-map-with-for-rule
48+
#:description
49+
"A `map` expression producing a list for a `for` loop can be fused with the loop."
50+
#:analyzers (list identifier-usage-analyzer)
51+
#:literals (define map in-list for for*)
52+
(~seq body-before ...
53+
(define ys:id (map function:fuseable-map-lambda list-expr:expr))
54+
((~or for-id:for for-id:for*)
55+
(~and original-clauses
56+
([y-var:id (in-list ys-usage:id)] remaining-clause ...+))
57+
for-body ...)
58+
body-after ...)
59+
60+
;; Check that ys is only used in the for loop, not elsewhere
61+
#:when (free-identifier=? (attribute ys) (attribute ys-usage))
62+
#:when (equal? (syntax-property #'ys 'usage-count) 1)
63+
64+
;; Generate the refactored code - fuse as nested clauses
65+
(body-before ...
66+
(for-id ([function.x (in-list list-expr)]
67+
[y-var (in-list (function.multi-body ...))]
68+
remaining-clause ...)
69+
for-body ...)
70+
body-after ...))
71+
72+
73+
;; Rule for when there are no remaining clauses - use internal definition
74+
(define-definition-context-refactoring-rule fuse-map-with-for-single-clause-rule
75+
#:description
76+
"A `map` expression producing a list for a `for` loop can be fused with the loop."
77+
#:analyzers (list identifier-usage-analyzer)
78+
#:literals (define map in-list for for*)
79+
(~seq body-before ...
80+
(define ys:id (map function:fuseable-map-lambda list-expr:expr))
81+
((~or for-id:for for-id:for*)
82+
(~and original-clauses
83+
([y-var:id (in-list ys-usage:id)]))
84+
for-body ...)
85+
body-after ...)
86+
87+
;; Check that ys is only used in the for loop, not elsewhere
88+
#:when (free-identifier=? (attribute ys) (attribute ys-usage))
89+
#:when (equal? (syntax-property #'ys 'usage-count) 1)
90+
91+
;; Generate the refactored code - use internal definition
92+
(body-before ...
93+
(for-id ([function.x (in-list list-expr)])
94+
(define y-var function.single-body)
95+
for-body ...)
96+
body-after ...))
97+
98+
99+
(define-refactoring-suite fuse-map-with-for
100+
#:rules (fuse-map-with-for-rule
101+
fuse-map-with-for-single-clause-rule))

0 commit comments

Comments
 (0)