I’ve seen a few examples where one can use a bare underscore in an expression, e.g.,
julia> map(_ -> "hello", 1:4)
4-element Array{String,1}:
"hello"
"hello"
"hello"
"hello"
julia> ["hello" for _ in 1:4]
4-element Array{String,1}:
"hello"
"hello"
"hello"
"hello"
julia> for _ in 1:4
println("hello")
end
hello
hello
hello
hello
But of course the following doesn’t work:
julia> _ = 2
2
julia> _
ERROR: all-underscore identifier used as rvalue
However, I haven’t seen anything in the docs that describes when you can use a bare underscore and when you can’t. Does anyone know where this is documented, or if it’s not documented, can someone spell out the rules regarding bare underscores in expressions?
_ is used when you syntactically need a variable, but never use it. This is because there are lots of times (like your examples above) where you want a loop, which syntactically would need a variable to be defined. The reason for this convention is that knowing that a variable won’t be used makes code easier to read often.
Yeah, I get that. My question is, exactly when does the Julia parser allow a bare underscore and when does it not? Perhaps there are some interesting use cases that I’m missing!
The error message gives a pretty decent description. You can never use _ as an rvalue, ie as something that goes on the right half of an equal sign. c++11 - What exactly is an R-Value in C++? - Stack Overflow does a pretty good job explaining (for c, but the terminology is the same).
To be honest, I don’t see how that has anything to do with whether something is an lvalue. Presumably, in the expression x -> 2x, the x on the left is an lvalue, but it’s not because “You don’t ever use its value.” My guess here is that function arguments can be considered lvalues in some sense? Furthermore, is it true that a bare underscore can be used anywhere that an lvalue can occur? Which begs the question, which expressions or parts of expressions in Julia are considered lvalues? The above SO post partially answers that question, but I think we’re still left with the question of what exactly counts as an lvalue in Julia and what exactly counts as an rvalue.
EDIT: I don’t think the answer to these questions is particularly urgent or important, but I am curious…
the variable x is an l-value because it get’s assigned to when I do a function call. That is, when I say f(2), a function scope will be created and inside that scope, there will be an assignment x = 2. So long as I don’t use x inside the function body, it’s perfectly legitimite to write
Yes, you can think of them as being assigned the arguments when the function is called.
Whether something is an lvalue or not is a syntactic property orthogonal to being _.
Personally, I don’t think in terms of lvalues/rvalues for _ (that’s the compiler’s job ). I would just go with this simple heuristic: if syntax requires a variable which you would otherwise not use, feel free to rename it _.
I’m wondering about that as well—when does the Julia parser allow a bare underscore.
I have found an example that (I think) is not explained by what has been discussed so far, and it so happens it’s something I’d like to use in a module I’m working on.
Say a physical property c is usually a function some other T, i.e., c:c(T), but for a specific model x, c degenerates to a constant. As to prove a concept, I’m coding the model x as a custom type, say: myModel.
I’ve defined a working c(x::myModel) which returns the proper constant, but for the sake of interface, I’d like to have a c(x::myModel, _) = c(x) version of it—the _ argument would be the other T that will never be used.
As for the breaking example, my original c(x::myModel) accepts keyword args. And I’ve found out that the existence of keyword arguments causes the julia-1.2.0 parser to complain about the _ argument:
A minimum example to illustrate keyword args causes complaints with _ args follows:
julia> c(x) = 3x # Fallback version (just a silly example)
c (generic function with 1 method)
julia> c(x, _) = c(x) # This works fine
c (generic function with 2 methods)
julia> c(x, _; double::Bool=false) = double ? 2c(x) : c(x) # _ is never used but parser complains
ERROR: syntax: all-underscore identifier used as rvalue around REPL[21]:1
Stacktrace:
[1] top-level scope at REPL[21]:1
julia> c(x, _; double::Bool) = double ? 2c(x) : c(x) # This also fails
ERROR: syntax: all-underscore identifier used as rvalue around REPL[22]:1
Stacktrace:
[1] top-level scope at REPL[22]:1
I’m wondering whether the existence of keyword args invalidating the use of _ arguments might be a bug.
EDIT: I’ve found out that c(x, ::T_Type; whatever) works fine and solves the case issue; however, it doesn’t answer OP’s question.
For any beginners who may find this post, I just wanna point out that the underscore can also be used as a digit grouping delimiter, to help readers visually parse big numbers. For example:
julia> 10_000 + 20_000
30000
Or you may prefer using the underscore to make it harder for people to read your code: