String operation

what does this do in julia? it does not give any ParseError, and when executed in REPL it just displays the name of the variable (a)

a = "abc"
"aa" a

OUTPUT:

julia> "aa" a
a

If I change the order like this it gives ParseError:

a = "abc"
a "aa"

and doing something similar with integers gives ParseError:

b = 123
12 b
3 Likes

Welcome to the discourse!
Well that indeed looks very odd and had me stumped for moment :sweat_smile: What happens is that this is the syntax for adding documentation to a symbol!
So if you write:

julia> "My string"  a
a

Then this string is used as documentation for the symbol a. You can see for example that it appears in the help prompt:

help?> a
search: a as any all abs ans Any axes atan asin asec any! all! acsc acot acos abs2 atanh atand asinh asind asech asecd ascii angle acsch acscd acoth acotd acosh acosd Array ARGS atomic

  My string
8 Likes

Ahhh, thanks!

It’s meant for functions/methods (which ARE also symbols?!), if not arbitrary symbols.

I’m not sure you would ever use this for variables, or rely on that possibility, should it then error?

EDIT: I recalled pi is a constant (irrational) “variable”, so at least used for help strings of such. Out of curiosity, does Julia have any (non-constant) global variables? I was trying to think of some, and could only think of a seed for rand, and rand is global, maybe the seed, though it’s strictly thread-local (at least with the new rand). Anyway this at least needs to be a possibility, and I suppose for package code too, even though globals are in general bad, with few exceptions. The REPL could still warn about it in case wrong…

The manual talks about adding documentation to many things and among them are also global variables :slight_smile:

So I was probably not fully correct above. The documentation is not really attached to the symbol (which I think is how Common Lisp does it) but to the global variable of that name.

3 Likes

The terminology is a bit confusing but I’d say it’s the other way: the documentation is attached to the binding (i.e. the symbol) and not the object. So you can have different docstrings for different aliases:

julia> "The type for a vector" const Vec = Vector
Vec

help?> Vec
search: Vec vec Vector VecOrMat VecElement eigvecs BitVector DenseVector DenseVecOrMat StridedVector StridedVecOrMat AbstractVector

  The type for a vector

julia> "The type for a Point" const Point = Vector
Point

help?> Vec
search: Vec vec Vector VecOrMat VecElement eigvecs BitVector DenseVector DenseVecOrMat StridedVector StridedVecOrMat AbstractVector

  The type for a vector

help?> Point
search: Point pointer pointer_from_objref codepoint unsafe_pointer_to_objref typejoin position ComposedFunction process_running

  The type for a Point

or for two bindings to the same array object:

julia> "array A" A = [1, 2, 3]
A

julia> "array B" B = A
B

help?> A
search: A as Any any all abs ARGS ans axes atan asin asec any! all! acsc acot acos abs2 Array atanh atand asinh asind asech asecd ascii angle

  array A

help?> B
search: B Bool big BLAS Base bind break begin bswap BitSet BigInt BitArray BigFloat binomial basename Broadcast BitVector BitMatrix bytes2hex

  array B

This seems not to seem to be true for functions:

julia> "foo docstring" 
       function foo(x)
           return x
       end
foo

julia> const f = foo
foo (generic function with 1 method)

julia> "another alias for foo" const f2 = foo
f2

help?> foo
search: foo floor fourthroot pointer_from_objref OverflowError RoundFromZero unsafe_copyto! functionloc StackOverflowError @functionloc OutOfMemoryError UndefKeywordError

  foo docstring

help?> f
search: f fd for fma fld foo fld1 fill fdio frexp foldr foldl flush floor float first fill! fetch fldmod filter falses finally foreach fldmod1 findmin findmax findall filter! function

  foo docstring

help?> f2
search: Float32 f2 Inf32 ComplexF32

  another alias for foo

So function appear to know about “their” docstrings. Which makes sense I guess, since we can document methods individually. So the documentation needs to be attached to the actual function objects and not their names.
But apart from that it is most likely the binding/name/symbol that is documented and not the object underneath.

This isn’t entirely accurate, in that if you make an alias for a value (such as a function) but don’t attach documentation, Julia will still find the original documentation. If you document the alias, it won’t.

julia> "a foo function" foo(a, b, c) = a * b * c
foo

help?> foo
search: foo floor fourthroot pointer_from_objref OverflowError RoundFromZero unsafe_copyto! functionloc StackOverflowError @functionloc

  a foo function

julia> bar = foo
foo (generic function with 1 method)

help?> bar
search: bar baremodule SubArray GlobalRef clipboard BitArray backtrace BasicREPL BitMatrix catch_backtrace AbstractREPL AbstractRange AbstractArray

  a foo function

julia> "a bar function" bar
bar

help?> bar
search: bar baremodule SubArray GlobalRef clipboard BitArray backtrace BasicREPL BitMatrix catch_backtrace AbstractREPL AbstractRange AbstractArray

  a bar function

Note that this isn’t function-specific behavior, it generalizes:

julia> "my struct" struct MyStruct end
MyStruct

help?> MyStruct
search: MyStruct

  my struct

julia> AnAlias = MyStruct
MyStruct

help?> AnAlias
search: AnAlias rationalize CanonicalIndexError

  my struct

julia> "with an alias" AnAlias
AnAlias

help?> AnAlias
search: AnAlias rationalize CanonicalIndexError

  with an alias
1 Like