Hell, I could argue that if you understand how conditionals return in lua, the former is easier to read as it's in plain English. "x equals 1 and 2 or 3" .. " x equals 1 question? 2 colon 3!" :P
Problem is that a lot of us are trained to consider "and" and "or" logical operations from other procedural languages. In lua they are not. They are indeed conditionals.
I think it's rather elegant once you have wrapped your head around it.
Things do get ugly once you get stuff like
a and b or c or d and e or f or g and y and z
Yes it's English but man, which thing has to be nil/false and what will be assigned isn't clear on inspection for those who don't have the precise definition of both operators fully understood. It also shows how mixing conditionals and assignment rules don't completely match how English works. In English "or" means that every condition in the "or" clause is a viable answer, in lua the first "or" term that is true is the answer.
In prodecural languages where and/or is a logical operation there isn't a real distinction, but because lua treats it as conditionals that actually evaluate/return values for truth it really does matter beyond bracket precedent how it's evaluated. You use them for conditional assignment and evaluation, and only in the simplest case as logical operations.
And to give the definition for of both operators (define FALSE as nil/false):
"X and Y" first evalutates the left argument. If it's FALSE, it returns FALSE, else it evaluates Y.
"A or B" first evaluates the left argument. If it's not FALSE it returns its result, else it evaluates B.
It's important to emphasize the evaluate bit here. That's why you can do:
dummy = myAddon and myAddon:someFunction() or print("Cannot find myAddon!")
This is basically the same as:
if myAddon then
dummy = myAddon:someFunction()
else
dummy = print("Cannot find myAddon!) -- this could well return nil(!) but it evaluates so you do get to see the print.
end
But that's the true elegance of this of course. By treating nil as false and and everything else as "true" and treat both as return values, logic operations and conditionals evaluation are naturally merged in lua thanks to loose types. As a side, if lua would allow to evaluate non-function type without error you could ditch the "dummy =" part but you cannot.
dummy = myAddon and myAddon:someFunction() or print("Cannot find myAddon!")
Dummy will be set to false/nil under the following conditions:
* myAddon is false/nil
* myAddon:someFunction() returns false/nil
* If either of those returns false/nil, then print('Cannot find myAddon!') will be evaluated.
Therefore, its not the same as
if myAddon then
dummy = myAddon:someFunction()
else
dummy = print('Cannot find myAddon!')
end
That's why that syntax sucks for readability; good old if then else statements are much easier to understand.
Maybe it's a Lua thing, but the expression (stuff and var2 or "") looks really dodgy to me. How would (stuff and var2) evaluate to var2 instead of stuff, or some combination of the two?
WTB the ?: operator thing from C, so that it's not so ambiguous what the result will be: blah = var1 .. " " .. (stuff ? var2 : "") .. (otherstuff ? var3 : "")
"X and Y or Z" has exactly the same semantics as C's ternary operator, provided that Y is not a boolean false. edit: by which I mean "X ? Y : Z"
Readability aside, neither Lua's and-or chain nor C's ternary op have exactly the same semantics as an explicit if-then-else chain in either language. They weren't meant to. Gotta use the right tool for the job.
Dummy will be set to false/nil under the following conditions:
* myAddon is false/nil
* myAddon:someFunction() returns false/nil
* If either of those returns false/nil, then print('Cannot find myAddon!') will be evaluated.
That's actually not correct. Let's go through the motions:
First operation is A and B which if A is nil will return A but to be used by the next operation a X or C and not to be assigned to dummy. Assignment to dummy happens when the whole clause has been considered!
To make this more clear let me write implied precedences:
Y = ((A and B) or C)
-- insert nil for A
Y = ((nil and B) or C)
Y = (nil or C)
Y = C
-- You never get A when nil in this conditional
-- insert true for A
Y = ((true and B) or C)
Y = (B or C)
-- You never get A when true either!
dummy will never be set to nil/false as result of the truth value of A because of the "or" clause. Remember if the left side of an or clause is nil/false, you always get the right side back. Hence I'd still claim that the if code isn't broken in the sense that you describe, but it actually is broken in another sense (let's get to that at the end...)
You would be right if the code was just:
dummy = A and B
Then dummy can be set as a result of A or as a result of B depending on whether A is nil/false or not. But that's not the conditionals chain we look at.
To make this more spicey though lets look at:
A and B or C or D
You'll see that you can always get any or terms, exactly when all previous terms are nil/false. Playing with and terms that you never get the left side of an and term as soon as there is any or term to its right.
Lets look when we have or terms to the left of an and term:
X or Y and Z
Here you can get Y under the following conditions:
X is nil and Y is nil. In that case it ignores Z. It's kinda easy to see that tail and clauses are the only ones where you can get the first and argument returned. A pure lonley and clause is a tail clause so that's kind of a universal rule of thumb.
But ultimately that's why tek was right to compare A and B or C with A ? B : C. In the C conditional you can never get A returned either.
There is a difference though and in that sense my if code is actually not equivalent!
What happens if A is true, B returns nil?
Correct if cascade would be:
if myAddon then
local tdummy = myAddon:someFunction()
if not tdummy then -- well I'm stretching the meaning of equivalence again ;)
dummy = print('Cannot find myAddon!')
else
dummy = tdummy
else
dummy = print('Cannot find myAddon!')
end
But I agree with you that it's tricky because one really has to watch the definition of and and or closely or you get the code mismatches I cooked up :P
The conjunction operator and returns its first argument if this value is false or nil; otherwise, and returns its second argument. The disjunction operator or returns its first argument if this value is different from nil and false; otherwise, or returns its second argument. Both and and or use short-cut evaluation; that is, the second operand is evaluated only if necessary.
Now if what you guys say was true, then this definition was incomplete.
Under functions it indeed says:
If control reaches the end of a function without encountering a return statement, then the function returns with no results.
Simply from the reference it is undefined how "no results" is treated in other syntactical constructions where some return value is needed like in arithmetic operations.
But in fact no return is treated as nil in arithmetic and logical expressions, as some simple tests show:
function test()
end
y = true and test()
print(y)
This prints nil. Per your argument it should print true.
y = 1 + test()
print(y)
Error 4: attempt to perform arithmetic on a nil value
You can replace test() with print("a") and get the same results.
The reason behind this is somewhere else in the lua reference. Lua knows the number of return values from a function, but will "append" nil to this list if more are requested. It's the same behavior as what lua does on function arguments.
This prints nil. Per your argument it should print true.
Why is that?
function test()
end
y = true and test()
print(y)
Now at the definition:
The conjunction operator and returns its first argument if this value is false or nil; otherwise, and returns its second argument.
Its first argument is clearly not false or nil, so it will always return its second argument, no matter what the first one was.
And yes, any argument list will always be padded with nil's to match the number of arguments required, in this case 1.
a, b, c, d = 1
is equivalent to
a, b, c, d = 1, nil, nil, nil
Of course the static 1 could as well be a function call that returns one value (or less then 4)
Although not important to the original discussion, but:
Remember that multiple return values of a function will be cut off if you manually pad the argument list.
function test()
return "a", "b", "c"
end
a, b, c, d = test(), "d"
This will NOT work, a will be set to "a", and b will be set to "d", any other return values of test() will be cut off.
There was a claim that it is possible to get a nil return from the left argument A of an "A and B or C" if C is a no-return value function.
I was pointing out that the claim that a function doesn't enter into such clauses if it is only evaluated but doesn't return anything is false. And it's quite clear with Jerry's explanation.
What mechanism would allow A to be returned if it would not return true if it only evaluated but didn't consider print, I don't know hence my interpretation of your position that "true and print()" will return true because I didn't see another interpretation how it would handle "nil and print()" if not by a mechanism to return the left value if the right value is just an eval and "returned nothing".
But we talk hypothetical mechanism here and surely you could claim that if a function returned no value it takes the left value only if it is nil (rewriting somehow how or works), which would leave this case undefined for the left value being true... because I assumed completeness, hence my assumption that your claim was that true and print() would return true. I grant you that I had to make an extra assumption there...
So granted, maybe you never meant "true and print()" returns true, but certainly there was an assumption of some mechanism regarding print() in logical clauses that didn't hold, which was what I was engaging with here.
Certainly we can keep discussion hypothetical mechanisms, but we quite clearly have the full actual mechanism at hand:
1) The definitions of logical operations are complete as stated in the refs
2) Functions can return nothing but if a return value is needed the arguments are padded with nils make them look like returning nils.
This means that my original claim that A and B or C can never ever return A is correct and there is no specific case depending on no return functions.
Err, all I was saying is that, if myAddon is false/nil, or the result of evaluating myAddon:doSomething() is false/nil, then print("myAddon not found!") will be evaluated and the entire thing will return false/nil.
...Actually, to be completely correct, the return value of the statement will be nil.
O'rly? I find looking at a huge "if chain" much more difficult than dissecting a one liner.
lies!
Hell, I could argue that if you understand how conditionals return in lua, the former is easier to read as it's in plain English. "x equals 1 and 2 or 3" .. " x equals 1 question? 2 colon 3!" :P
I think it's rather elegant once you have wrapped your head around it.
Things do get ugly once you get stuff like
a and b or c or d and e or f or g and y and z
Yes it's English but man, which thing has to be nil/false and what will be assigned isn't clear on inspection for those who don't have the precise definition of both operators fully understood. It also shows how mixing conditionals and assignment rules don't completely match how English works. In English "or" means that every condition in the "or" clause is a viable answer, in lua the first "or" term that is true is the answer.
In prodecural languages where and/or is a logical operation there isn't a real distinction, but because lua treats it as conditionals that actually evaluate/return values for truth it really does matter beyond bracket precedent how it's evaluated. You use them for conditional assignment and evaluation, and only in the simplest case as logical operations.
And to give the definition for of both operators (define FALSE as nil/false):
"X and Y" first evalutates the left argument. If it's FALSE, it returns FALSE, else it evaluates Y.
"A or B" first evaluates the left argument. If it's not FALSE it returns its result, else it evaluates B.
It's important to emphasize the evaluate bit here. That's why you can do:
This is basically the same as:
But that's the true elegance of this of course. By treating nil as false and and everything else as "true" and treat both as return values, logic operations and conditionals evaluation are naturally merged in lua thanks to loose types. As a side, if lua would allow to evaluate non-function type without error you could ditch the "dummy =" part but you cannot.
* myAddon is false/nil
* myAddon:someFunction() returns false/nil
* If either of those returns false/nil, then print('Cannot find myAddon!') will be evaluated.
Therefore, its not the same as
That's why that syntax sucks for readability; good old if then else statements are much easier to understand.
"X and Y or Z" has exactly the same semantics as C's ternary operator, provided that Y is not a boolean false. edit: by which I mean "X ? Y : Z"
Readability aside, neither Lua's and-or chain nor C's ternary op have exactly the same semantics as an explicit if-then-else chain in either language. They weren't meant to. Gotta use the right tool for the job.
That's actually not correct. Let's go through the motions:
First operation is A and B which if A is nil will return A but to be used by the next operation a X or C and not to be assigned to dummy. Assignment to dummy happens when the whole clause has been considered!
To make this more clear let me write implied precedences:
dummy will never be set to nil/false as result of the truth value of A because of the "or" clause. Remember if the left side of an or clause is nil/false, you always get the right side back. Hence I'd still claim that the if code isn't broken in the sense that you describe, but it actually is broken in another sense (let's get to that at the end...)
You would be right if the code was just:
Then dummy can be set as a result of A or as a result of B depending on whether A is nil/false or not. But that's not the conditionals chain we look at.
To make this more spicey though lets look at:
You'll see that you can always get any or terms, exactly when all previous terms are nil/false. Playing with and terms that you never get the left side of an and term as soon as there is any or term to its right.
Lets look when we have or terms to the left of an and term:
Here you can get Y under the following conditions:
X is nil and Y is nil. In that case it ignores Z. It's kinda easy to see that tail and clauses are the only ones where you can get the first and argument returned. A pure lonley and clause is a tail clause so that's kind of a universal rule of thumb.
But ultimately that's why tek was right to compare A and B or C with A ? B : C. In the C conditional you can never get A returned either.
There is a difference though and in that sense my if code is actually not equivalent!
What happens if A is true, B returns nil?
Correct if cascade would be:
But I agree with you that it's tricky because one really has to watch the definition of and and or closely or you get the code mismatches I cooked up :P
Its not a generic explanation for the principle, but for this case (and his answer was tailored for this case), it is correct.
Here's from the lua reference:
Now if what you guys say was true, then this definition was incomplete.
Under functions it indeed says:
Simply from the reference it is undefined how "no results" is treated in other syntactical constructions where some return value is needed like in arithmetic operations.
But in fact no return is treated as nil in arithmetic and logical expressions, as some simple tests show:
This prints nil. Per your argument it should print true.
Error 4: attempt to perform arithmetic on a nil value
You can replace test() with print("a") and get the same results.
Now at the definition:
Its first argument is clearly not false or nil, so it will always return its second argument, no matter what the first one was.
And yes, any argument list will always be padded with nil's to match the number of arguments required, in this case 1.
is equivalent to
Of course the static 1 could as well be a function call that returns one value (or less then 4)
Although not important to the original discussion, but:
Remember that multiple return values of a function will be cut off if you manually pad the argument list.
This will NOT work, a will be set to "a", and b will be set to "d", any other return values of test() will be cut off.
I was pointing out that the claim that a function doesn't enter into such clauses if it is only evaluated but doesn't return anything is false. And it's quite clear with Jerry's explanation.
What mechanism would allow A to be returned if it would not return true if it only evaluated but didn't consider print, I don't know hence my interpretation of your position that "true and print()" will return true because I didn't see another interpretation how it would handle "nil and print()" if not by a mechanism to return the left value if the right value is just an eval and "returned nothing".
But we talk hypothetical mechanism here and surely you could claim that if a function returned no value it takes the left value only if it is nil (rewriting somehow how or works), which would leave this case undefined for the left value being true... because I assumed completeness, hence my assumption that your claim was that true and print() would return true. I grant you that I had to make an extra assumption there...
So granted, maybe you never meant "true and print()" returns true, but certainly there was an assumption of some mechanism regarding print() in logical clauses that didn't hold, which was what I was engaging with here.
Certainly we can keep discussion hypothetical mechanisms, but we quite clearly have the full actual mechanism at hand:
1) The definitions of logical operations are complete as stated in the refs
2) Functions can return nothing but if a return value is needed the arguments are padded with nils make them look like returning nils.
This means that my original claim that A and B or C can never ever return A is correct and there is no specific case depending on no return functions.
...Actually, to be completely correct, the return value of the statement will be nil.
I thought you meant to say that it returns nil because myAddon is false. However ultimately,it returns nil because print() happens to return nil.
I basically read your "is set under the following conditions" as meaning "is set from the values of". My bad absolutely.
I see your point and we are really saying the same thing.