My understanding of short circuit evaluation is that an expression is only called when needed in an if statement. Does Go follow this?
For instance, would I get better performance on average from:
if !isValidQueryParams(&queries) || r == nil || len(queries) == 0 {
return "", fmt.Errorf("invalid querystring")
}
...to this:
if r == nil || len(queries) == 0 || !isValidQueryParams(&queries) {
return "", fmt.Errorf("invalid querystring")
}
...since isValidQueryParams is a function with much more overhead than r == nil or testing the length of a map?
i.e. will the interpreter evaluate r == nil first, see it's true and not bother to evaluate the other conditions?
EDIT: Incorrectly referred to short circuit evaluation as lazy evaluation
Thank you to Kostix and mkrieger for their answers - they are correct, I'm referring to short circuit evaluation and not lazy evaluation.
Go does implement normal short circuit evaluation, as can be deduced with the following code:
package main
import "fmt"
func main() {
for i := 0; i < 10; i++ {
if testFunc(1) || testFunc(2) {
// do nothing
}
}
}
func testFunc(i int) bool {
fmt.Printf("function %d called\n", i)
return true
}
...which will always give:
$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called
$ function 1 called
This is called short circuit evaluation. According to this tutorial, the boolean operators use this:
Although in the Go language specification it does not explicitly
state that Go uses short circuit evaluation, it does mention that
Logical operators apply to boolean values and yield a result of the same type as the operands. The right operand is evaluated
conditionally.
Here is a quick example to prove that Go uses short circuit evaluation
[…]
What you're referring to is called "short circut evaluation"—that is, the subexpressions are evaluated using the normal associativity rules only for as long as the complete result is available and evaluation of the rest of the expressions won't change it according to the rules of the binary operator(s) in question.
Go does implement short circuit evaluation of logical expressions (see the other answer).
(#icza commented: Somewhat related: there is short circuit evaluation in Go code, but Go's template engine does not use short circuit evaluation. Details: Golang template and testing for Valid fields.)
"Lazy evaluation" is completely another thing—usually implemented in the so-called "functional" programming languages, and it's not directly implemented in Go.
Having said that, I'd note that while Go does not have direct (as with the syntax and runtime) support for lazy evaluation, it may be used where needed.
For instance, you may have a goroutine reading potentially infinite number of items from a channel, and processing them in one way or another, and another goroutine—or several of them—producing these values and sending them through the channel. This way, the values only "materialize" at the receiving end no faster than they are actually ready to be handled.
Related
I have the following code:
package main
import (
"fmt"
)
func main() {
switch {
case 1 == 1:
fmt.Println("1 == 1")
fallthrough
case 2 == 1:
fmt.Println("2 == 1")
}
}
Which prints both lines on the go playground - see example here. I would have expected the fallthrough statement to include evaluation of the next case statement, but this seems not to be the case.
Of course, I can always use a bunch of if statements, so this is not a real impediment, but I am curious what the intention here is, since this seems to me to be a non-obvious result.
Anyone care to explain? For example: in this code, how can I get the 1st and 3rd cases to execute?
Switch is not a bunch of ifs. It's more akin to if {} else if {} construct, but with a couple of twists - namely break and fallthrough. It's not possible to make switch execute first and third cases - a switch does not check each condition, it finds first match and executes it. That's all.
It's primary purpose is to walk through a list of possible values and execute a different code for each value. In fact, in C (where switch statement came from) switch expression can only be of integral type and case values can only be constants that switch expression will be compared too. It's only relatively recently, languages started adding support for strings, boolean expressions etc in switch cases.
As to fallthrough logic it also comes from C. There is no fallthrough operator in C. In C execution falls through into next case (without checking case values) unless break operator encountered. The reason for this design is that sometimes you need to do something special and then do same steps as in another case. So, this design merely allows that. Unfortunately, it's rather rarely useful, so falling through by default was causing more trouble when programmer forgotten to put a break statement in, then actually helping when truly omitted that break intentionally. So, many modern languages change this logic to never fall through by default and to require explicit fallthrough statement if falling through is actually required.
Unfortunately, it's a it hard to come up with a non contrived example of fallthrough being useful that would be short enough to fit into an answer. As I said it's relatively rare. But sometimes you need to write code similar to this:
if x == a || x == b {
if x == a {
// do action a
}
// do action ab
} else if x == c {
// do action c
} else if x == d {
// do action d
}
In fact, I needed code of similar structure quite recently in one of my projects. So, I used switch statement instead. And it looked like this:
switch x {
case a: // do action a
fallthrough
case b: // do action ab
case c: // do action c
case d: // do action d
}
And your switch from the question is functionally equivalent to this:
if 1 == 1 || 2 == 1 {
if 1 == 1 {
fmt.Println("1 == 1")
}
fmt.Println("2 == 1")
}
Presumably, Go's fallthrough behavior is modeled after C, which always worked like this. In C, switch statements are just shorthands for chains of conditional gotos, so your particular example would be compiled as if it was written like:
# Pseudocode
if 1 == 1 goto alpha
if 2 == 1 goto beta
alpha:
fmt.Println("1 == 1")
beta:
fmt.Println("2 == 1")
As you can see, once execution enters the alpha case, it would just keep flowing down, ignoring the beta label (since labels by themselves don't really do anything). The conditional checks have already happened and won't happen again.
Hence, the non-intuitive nature of fallthrough switch statements is simply because switch statements are thinly veiled goto statements.
From the language spec:
A "fallthrough" statement transfers control to the first statement of the next case clause in an expression "switch" statement. It may be used only as the final non-empty statement in such a clause.
That seems to perfectly describe your observed behavior.
So I was watching this video on the Go language - https://www.youtube.com/watch?v=p9VUCp98ay4 , and at around 6:50 a guy asks a question about why they implemented if's as statements and not expressions. What is the difference between those two implementations? As far as I know, I've never had to change the way I use a conditional based on the language.
Edit: and what does he mean that "you need values rather than variables" in his question?
The difference between expressions and statements is that expressions produce a value and thus can be used in places where values are required. So expressions can be used as values for variables, arguments to functions or operands to operators. Statements can't.
and what does he mean that "you need values rather than variables" in his question?
I assume that by vals he means constants (which are called vals in Scala for example).
If if were an expression, you could do this:
const myValue = if condition { value1 } else { value2 }
Since if is not an expression, you have to do this:
var myValue
if condition {
myValue = value1
} else {
myValue = value2
}
So you needed to make your variable mutable (use var instead of const), which is what the person asking the question likely meant.
You can get the same code elegance that you would get from if expression by using an instantly invoked function (IIF) combined with if statements in GO. I'm not a GO programmer (mainly typescript) so please let me know if this is bad for performance for any reason.
func main() {
amIHungry := true
didMyPaycheckComeInYet := false
choice := func() string {
if(!amIHungry && !didMyPaycheckComeInYet){
return "stay-home"
}
if(!amIHungry && didMyPaycheckComeInYet){
return "buy-new-keyboard"
}
if(amIHungry && !didMyPaycheckComeInYet){
return "make-ramen"
}
return "taco-bell-time"
}()
println(choice)
}
and then later on in your program, rather than having a bunch of out of context states, you can have a simplified "choice" to choose your app logic.
if(choice == "taco-bell-time"){
println("Order a bean burrito, but with black beans,")
}
is a little easier to reason about than
if(amIHungry && didMyPaycheckComeInYet){
println("Order a bean burrito, but with black beans,")
}
Suppose we had the following:
Var idx;
Func out;
out(idx) = select(idx == 0, VAL_1, select(idx == 1, VAL_2, VAL3));
It would be nice to be able to force Halide to use if/then/else constructs for this in the loop body, instead of selects. I was assuming that this is done using the specialize() scheduling command:
out.specialize(idx == 0);
out.specialize(idx == 1);
However, this seems to be forbidden:
"Error at (...): Specialization condition (...) depends on Var or RVar idx.
Specialization conditions may not depend on any Vars or RVars.
Is there a particular reason why this limitation exists? Or maybe some other way to reach the intended behavior, other than unrolling?
Thanks and kind regards,
Sander
Specialization puts an if statement outside of the outermost loop, which means that other Funcs that are compute_at the Func being specialized get included in the specialization.
The only way to get an if statement with a condition that depends on a loop variable is RDom::where, and there's no way to give it an else clause, so this may not be possible. Could you explain more why an if statement is better than unrolling in your case? Is idx parallel?
I would unroll(idx). The select will then be using constants for the conditional and will be optimized out by Halide.
if(A) then if(B)
vs
if(A and B)
Which is better to use and why ?
Given:
if (a) {
if (b) {
/// E1
}
/// E2
} else {
// E3
}
One may be tempted to replace it with:
if (a && b) {
/// E1
} else {
// E3
}
but they are not equivalent(a = true and b = false shows the counter-argument for it)
Other than that, there is no reason not to chain them if the language allows short circuits operations like AND, OR. And most of them allows it. Expressions are equivalent and you can use the chained form to improve code readability.
It depends on your specific case but generally:
1) if (A and B) looks better/cleaner. It's immediately clear that the following block will execute if both A and B apply.
2) if(A) then if(B) is better in cases when you want to do something also when A applies, but B doesn't. In other words:
if (A):
if (B):
# something
else:
# something else
generally looks better than
if (A and B):
# something
if (A and not B):
# something else
You've tagged this 'algorithm' and 'logic' and from that perspective I would say little difference.
However in practical programming languages there may or may not be a question of efficiency (or even executability).
Programming languages like C, C++ and Java guarantee that in the expression A && B that A is evaluated first and if false B is not evaluated.
That can make a big difference if B is compuatationally expensive or is invalid if A is false.
Consider the following C snippet:
int*x
//....
if(x!=NULL&&(*x)>10) {
//...
Evaluating (*x) when x==NULL will very likely cause a fatal error.
This 'trick' (called short-circuit evaluation) is useful because it avoids the need to write the slightly more verbose:
if(x!=NULL){
if((*x)>10){
Older versions of VB such as VB6 are infamous for not making the short circuits.
The other one is that B in A || B will not be evaluated if A is true.
Discussion about support:
Do all programming languages have boolean short-circuit evaluation?
In any language that provides short-circuit and has an optimizing compiler you can assume there is unlikely to be any difference in code efficiency and go with the most readable.
That is normally if(A&&B).
Here is the program to find the factorial of a number in Go:
func factorial(x uint) uint {
if x == 0 {
return 1
}
return x * (factorial(x - 1))
}
The output for this function when called on input 5 is 120. However, if I add an else statement I get an error.
func factorial(x uint) uint {
if x == 0 {
return 1
} else {
return x * (factorial(x - 1))
}
}
Error : function ends without a return statement
I added a return at the end :
func factorial(x uint) uint {
if x == 0 {
return 1
} else {
return x * (factorial(x - 1))
}
fmt.Println("this never executes")
return 1
}
and I get back the expected output of 120.
Why would the second case cause an error? Why in the third case even though the function never reaches the last return 1, it computes the correct output?
This is a well known problem of the compiler.
There is even an issue logged : http://code.google.com/p/go/issues/detail?id=65
In the words of one of the authors of the Go language:
The compilers require either a return or a panic to be lexically last
in a function with a result. This rule is easier than requiring full
flow control analysis to determine whether a function reaches the end
without returning (which is very hard in general), and simpler than
rules to enumerate easy cases such as this one. Also, being purely
lexical, the error cannot arise spontaneously due to changes in values
such as constants used in control structures inside the function.
-rob
From another comment in golang-nuts, we can infer it's not going to be "fixed" soon :
It's not a bug, it's a deliberate design decision.
-rob
Note that other languages like Java have rules allowing this else.
March 2013 EDIT - It just got changed in Go1.1 :
Before Go 1.1, a function that returned a value needed an explicit
"return" or call to panic at the end of the function; this was a
simple way to make the programmer be explicit about the meaning of the
function. But there are many cases where a final "return" is clearly
unnecessary, such as a function with only an infinite "for" loop.
In Go 1.1, the rule about final "return" statements is more
permissive. It introduces the concept of a terminating statement, a
statement that is guaranteed to be the last one a function executes.
Examples include "for" loops with no condition and "if-else"
statements in which each half ends in a "return". If the final
statement of a function can be shown syntactically to be a terminating
statement, no final "return" statement is needed.
Note that the rule is purely syntactic: it pays no attention to the
values in the code and therefore requires no complex analysis.
Updating: The change is backward-compatible, but existing code with
superfluous "return" statements and calls to panic may be simplified
manually. Such code can be identified by go vet.
And the issue I mentioned is now closed with status "Fixed".