Strange (to me, at least) result in string interpolation

I stumbled across this today while debugging a little macro I am writing. If someone could explain this to me, I would really appreciate it. It’s not a problem for my code, but I’m quite curious to understand.

My macro itself isn’t very relevant, but it parses its arguments and builds an array of Float64s, then passes the transpose of this array as one of several parameters to a function in the returned expression. This is where I noticed the unexpected.

A minimal example to reproduce:

julia> a = [1.0]
1-element Vector{Float64}:

julia> a'
1×1 adjoint(::Vector{Float64}) with eltype Float64:

julia> transpose(a)
1×1 transpose(::Vector{Float64}) with eltype Float64:

julia> println("$(a)")

julia> println("$(a')")
[1.0;;] # Why the semi-colons???

julia> println("$(transpose(a))")
[1.0;;] # Why the semi-colons???

julia> b = [1.0, 2.0]
2-element Vector{Float64}:

julia> b'
1×2 adjoint(::Vector{Float64}) with eltype Float64:
 1.0  2.0

julia> transpose(b)
1×2 transpose(::Vector{Float64}) with eltype Float64:
 1.0  2.0

julia> println("$(b)")
[1.0, 2.0]

julia> println("$(b')")
[1.0 2.0] # No semi-colons here

julia> println("$(transpose(b))")
[1.0 2.0] # Or here

Why does the result for a in the println()s include the two semi-colons? There are no semi-colons if the original array contains more than one entry, as illustrated with the b example.

It also doesn’t matter if I use the adjoint operator or the transpose function. It seems to be a result of the string interpolation, but only for the transpose/adjoint.

It has nothing to do with interpolation:

julia> println(transpose([1.0]))

julia> repr(transpose([1.0]))

julia> show(stdout, transpose([1.0]))

and it happens for any 1x1 matrix:

julia> println(fill(1.0, 1,1))

This is how show (hence repr, which is used in string interpolation) is defined for a 1x1 matrix — the extra semicolons tell Julia’s parser that this is a 1x1 matrix (a 2d array) and not a 1-component vector (a 1d array):

julia> [1.0;;]
1×1 Matrix{Float64}:

julia> [1.0]
1-element Vector{Float64}:

See array literal concatenation in the Julia manual.

PS. Normally in macros and metaprogramming, you should never need to deal with expressions as strings, as opposed to using the Expr objects directly. Strings are fragile.

1 Like

This, I fully believe. This is my first ever attempt at meta programming, and it has been a fun and interesting ride already. But I am fully aware that books could be written about what I don’t know about the topic. Lot’s more to learn, and I’m looking forward to it.