Benny
June 14, 2022, 8:34am
#1
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
Sukera
June 14, 2022, 10:12am
#2
I haven’t looked into this particular example too deeply, but probably
JuliaLang:master
← jakobnissen:colondocs
opened 09:38AM - 23 May 22 UTC
Unary `:` was poorly documented previously. The previous docs stated it would
r… eturn a Symbol, but in fact also Expr objects and literal values can be
returned from code quoting.
See #43054
and
opened 12:16PM - 12 Nov 21 UTC
doc
So, what does unary `:` do? The docstring of `:` states
```
: is also used in … indexing to select whole dimensions and for Symbol literals, as in e.g. :hello.
```
. Julia's manual mentions a second use, not mentioned in the docstring:
```
The : character, followed by paired parentheses around a single statement of Julia code, produces an Expr object based on the enclosed code.
```
Perhaps more bizarrely, at least to me, sometimes `:` does neither - in fact it does nothing at all:
```
julia> :1
1
```
And _when exaclty_ is does nothing at all is completely inscrutable, at least to me. Is it perhaps only with literals?
Nope:
```
julia> :1111111111111111111111
:(1111111111111111111111)
```
What is even a literal? Julia's manual talks about e.g. array literals, but
```
julia> :([])
:([])
```
If the answer is: "The `:` operator does nothing if the parser recognizes the following string being a literal value, without needing to call any macros or such", then that looks like a leaky abstraction to me - the precise behaviour of what the parser knows at certain times impact the value returned in the REPL when the user types something in.
This discussion is not just academic, it was sparked by a nasty bug encountered by a person used to coding in Python, who used the index `[:2]` instead of `[1:2]`, and was very surprised to find that `:2` evaluated to just `2`.
Can this behaviour be documented? It's all very confusing to me.
Edit: After a long and patient explanation by @Seelengrab of what `:` does, perhaps it would be much clearer if it said something along the lines of ":x is equivalent to `Meta.parse("x")`, and can return an `Expr`, a `Symbol` or a literal value."
are relevant. The interpolation part you encounter is the remaining edge case jeff mentions in that issue…
1 Like