eval refitted

This commit is contained in:
2026-05-27 15:28:23 +02:00
parent ad6d47023b
commit efe8e6769f
3 changed files with 196 additions and 24 deletions
+53 -10
View File
@@ -83,8 +83,11 @@ self-call.
The generator is best understood as a source-to-source translator over syntax.
The input is converted to datum form and matched against the supported surface
language. The Racket code is not evaluated. This has a few important
consequences:
language. The Racket code is normally not evaluated. The deliberate exception
is @racket[(eval racket-expr)], which interpolates a Racket value into the
JavaScript source text. The expression is evaluated in the lexical context of
the @racket[js] or @racket[js/expression] use and its value is emitted as a
JavaScript literal. This has a few important consequences:
@compact-items[
@item{Only forms known to js-maker are translated specially. Unknown calls are
@@ -100,6 +103,46 @@ consequences:
helper functions/IIFEs to preserve semantics.}
]
@subsection{Racket value interpolation with eval}
The form @racket[(eval racket-expr)] is an interpolation escape hatch inherited
from the original transformer. It evaluates @racket[racket-expr] as Racket in
the use-site lexical context and then splices the resulting value into the
JavaScript source as a literal. This makes surrounding Racket bindings visible
to the interpolation expression.
@(rkt+js
#<<RKT
(let ([x 10]
[y 20])
(js (let ([a (eval (* x y))])
(return (* a a)))))
RKT
#<<JS
{
let a = 200;
return (a * a);
}
JS
)
@(rkt+js
#<<RKT
(js/expression (array (eval (+ 1 2))
(eval (string-append "a" "b"))))
RKT
#<<JS
[3, "ab"]
JS
)
This is not JavaScript @tt{eval}. To call JavaScript @tt{eval}, call the
JavaScript function explicitly, for example @racket[(send window eval "1 + 2")].
Racket-side @racket[eval] is best used for constants, generated literal data,
and small configuration values that are known while the JavaScript source is
being constructed. It should not be used for run-time browser state, DOM access,
or user input.
@section{Supported expression and statement forms}
The following Racket forms are supported as either expressions, statements, or
@@ -116,6 +159,7 @@ both, depending on context:
@item{@racket[when], @racket[unless], @racket[while], @racket[for],
@racket[for/list], @racket[for/vector], and @racket[for/fold].}
@item{@racket[with-handlers] for the generic @racket[exn?] case.}
@item{@racket[(eval racket-expr)] for Racket-side value interpolation into JavaScript literals.}
@item{Interop forms such as @racket[send], @racket[new], @racket[js-ref],
@racket[js-dot], @racket[set-prop!], @racket[delete-prop!],
@racket[js-delete], @racket[array], @racket[object],
@@ -732,14 +776,13 @@ harness rather than hiding raw JavaScript inside the feature implementation.
@section{Further examples}
The companion document @filepath{scrbl/usecases.scrbl} contains practical
examples such as DOM manipulation, @tt{Set}, currying, object destructuring,
timers, @tt{fetch}, sorting, binary search, and map-based counting. Each use
case shows the Racket/js-maker source, representative JavaScript output, and the
behavior tested by the regression suite.
The companion @hyperlink["../usecases/index.html"]{use-case manual} contains
practical examples such as DOM manipulation, @tt{Set}, currying, object
destructuring, timers, @tt{fetch}, sorting, binary search, and map-based
counting. Each use case shows the Racket/js-maker source, representative
JavaScript output, and the behavior tested by the regression suite.
When the documentation is rendered with @exec{raco scribble}, the use-case
manual is normally emitted as a separate document next to this one. The explicit
file name is used here instead of a cross-document reference because the latter
The source file for that companion manual is @filepath{scrbl/usecases.scrbl}.
The relative link above is used instead of @racket[other-doc] because the latter
can render as an unresolved @tt{(part ... "top")} tag when the two documents are
built outside Racket's installed documentation index.