# Nested expressions rules seem different when `\$` involved

So normally when nesting an expression into an expression, it seems like it doesn’t matter if the latter is written as a quoted `:( ___ )` or an `Expr(:quote, ___ )`. There’s a difference when the former is written as a quote or an `Expr`, but the nested expressions `eval` to the same inner expression.

``````julia> @macroexpand x + 1
:(x + 1)

julia> :( :(x+1) ) == Expr(:quote, :(x+1) )
true
julia> eval( :( :(x+1) ) )
:(x + 1)

julia> :( :(x+1) ) == :( Expr(:call, :+, :x, 1) )
false
julia> eval( :( Expr(:call, :+, :x, 1) ) )
:(x + 1)
``````

But things seem to change when the symbol `\$` is involved. First, there’s no way to quote the expression `Expr(:\$, :x)` parsed from the source code `\$x`, which kind of makes sense because `\$x` is a syntax error. Second, putting `Expr(:\$, :x)` in either a quoted `:( ___ )` or an `Expr(:quote, ___ )` now makes a difference. The former is more like a nested expression because it `evals` to the inner `Expr(:\$, :x)`. The latter is almost what happens in `@eval \$x` (and why I started on this train of thought). Third, the latter is somehow equivalent to nesting a different expression `:(\$x)` in a `:( ___ )`. I can’t really make sense of this.

``````julia> x = 3
3
julia> @macroexpand \$x
:(\$(Expr(:\$, :x)))
julia> Expr(:\$, :x) # REPL print is redundant
:(\$(Expr(:\$, :x)))

julia> :( Expr(:\$, :x) ) == Expr(:quote, Expr(:\$, :x) ) # ?!
false
julia> eval( :( Expr(:\$, :x) ) )
:(\$(Expr(:\$, :x)))
julia> eval( Expr(:quote, Expr(:\$, :x) ) )
3

julia> Expr(:quote, Expr(:\$, :x) ) == :( :(\$x) ) # ?!
true
``````

I haven’t looked into this particular example too deeply, but probably

https://github.com/JuliaLang/julia/pull/45426

and

https://github.com/JuliaLang/julia/issues/43054

are relevant. The interpolation part you encounter is the remaining edge case jeff mentions in that issue…

1 Like

I believe there is a distinction between `:( Expr(:\$, :x) )` and `Expr(:quote, Expr(:\$, :x) )`

For a simpler example, consider we have `x=1` and compare `:(x+1)` and `Expr(:quote, x+1)`.
the `x+1` in `:(x+1)` is just a random string of letters; The compiler(or parser, for that matter) only interprets the meaning when `:(x+1)` gets evaluated.

`x+1` in `Expr(:quote, x+1)`, on the other hand, is computed immediately before we `eval()` it.
As a result, `Expr(:quote, x+1)` equals `Expr(:quote, 2)`

Similarly, when we do `:( Expr(:\$, :x) ) |> eval`, the parser takes the string “Expr(:\$, :x)”
, converts it to `Expr` object and evaluates the `Expr` object to get the final result, which is `Expr(:dollar, :x)` itself. This is what occurs when we type `Expr(:dollar, :x)` in the REPL and the ENTER key.

Howerver in `Expr(:quote, Expr(:\$, :x) ) `, the `Expr(:\$, :x)` is created (before `eval()` ever occurs). When we use `Expr(:quote, Expr(:\$, :x) ) |> eval`, the parser attempts to evaluate the previously constructed `Expr` object, `Expr(:\$, :x)`,and the result of doing that is to refer the variable `x`. I believe extracting ___ from `Expr(:\$, :(___)` would be a useful mental modelling of this action.

So rather than `Expr(:quote, Expr(:\$, :x))` I believe one possible equivalent form for `:( Expr(:\$, :x) )` is `Expr(:quote, Expr(:\$, :(Expr(:\$, :x))))` .

`Expr(:quote, Expr(:\$, :x))` corresponds to `quote; x; end`, rather than `quote; Expr(:\$, :x); end` or `:(Expr(:\$, :x))`

I said “equivalent”, rather than “equal”, because for example `:(x+1) != quote; x+1; end` but they both mean the same thing. But these are just my thoughts.
{"extraction_info": {"found_math": false, "script_math_tex": 0, "script_math_asciimath": 0, "math_annotations": 0, "math_alttext": 0, "mathml": 0, "mathjax_tag": 0, "mathjax_inline_tex": 0, "mathjax_display_tex": 0, "mathjax_asciimath": 0, "img_math": 0, "codecogs_latex": 0, "wp_latex": 0, "mimetex.cgi": 0, "/images/math/codecogs": 0, "mathtex.cgi": 0, "katex": 0, "math-container": 0, "wp-katex-eq": 0, "align": 0, "equation": 0, "x-ck12": 0, "texerror": 0, "math_score": 0.9043505787849426, "perplexity": 3287.054877201675}, "config": {"markdown_headings": true, "markdown_code": true, "boilerplate_config": {"ratio_threshold": 0.3, "absolute_threshold": 10, "end_threshold": 15, "enable": true}, "remove_buttons": true, "remove_image_figures": true, "remove_link_clusters": true, "table_config": {"min_rows": 2, "min_cols": 3, "format": "plain"}, "remove_chinese": true, "remove_edit_buttons": true, "extract_latex": true}, "warc_path": "s3://commoncrawl/crawl-data/CC-MAIN-2022-49/segments/1669446710771.39/warc/CC-MAIN-20221130192708-20221130222708-00604.warc.gz"}