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.
Related
package main
import "fmt"
func main() {
var age int
fmt.Scanf("%d", &age)
// Code your switch or if...else-if statement here.
switch age {
case 1:
age <= 14
fmt.Println("Toy Story 4")
case 2:
age <= 18
fmt.Println("The Matrix")
case 3:
age <= 25
fmt.Println("John Wick")
case 4:
age <= 35
fmt.Println("Constantine")
case 5:
age > 35
fmt.Println("Speed")
default:
fmt.Println("Error: Unknown age")
}
}
I get a yellow marker in the IDE for the scanf, flagging an unhandled error. Also, all the cases have the first line flagged red, preventing to compile. The error I get is age <= 14 is evaluated, but not used. The same is true for all statements. I have searched the web and looked at examples, and from what I can see, the code is, as the materials and examples state. Here is a screenshot from Go Land (JetBrains):
Does anybody have an idea? Also, if you answer, please keep in mind, that this is a lesson from a Go course and I have to use scanf. I could change the switch to an else if, which is likely going to fix the issue, but I am kind of curious, about why this is happening and how I can fix it. :)
age <= 14 is not a valid Go expression, though it could be used as part of one. Go is very particular about all values being consumed.
This isn't the correct way to write a case with <= operators.
switch age {
case 1:
age <= 14
fmt.Println("Toy Story 4")
...
}
That case matches if the value of age is 1. You want to use a switch style like this:
switch {
case age <= 14:
fmt.Println("Toy Story 4")
case age <= 18:
...
...
}
To add to what Daniel wrote — basically the switch statement (unless it's a type switch but let's not digress) has two forms:
switch { // note the absense of any expression here
case bool_expr_1:
...
case bool_expr_2:
...
and
switch any_expr {
case value_to_match_1:
...
case value_to_match_2:
...
In the first form, expressions in each case branch are evaluated top-to-bottom, and the first which evaluates to true, "wins"—that is, the code in that branch is executed.
In the second form, the expression is evaluated to produce a value which is then compared exactly with the value in each case branch.
You have tried to sort-of combine both forms, which won't work.
You might have thought that those numbers in the case branches is the order of how the branches should be checked but no, that's not it.
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.
I realize that it's not valid ruby but what would be the technical hurdles to implement the below functionality into the Ruby core language (of say v2.3)?
variable = 1 if condition else -1
I'd also like to allow the following for more generic use.
variable = { 1 } if condition else { -1 }
I'm very open to requiring an "end" at the end.
I get that a ternary can easily accomplish this but I'm looking for a more readable inline-if that allows an else.
I get that I can make a function which does this via any number of styles but I'd prefer to have it as readable as possible.
Thanks.
EDIT: I hate editing questions for obvious reasons.
In response to the question of how the generic option is more ruby-esque, see the below example (I needed newlines).
variable = {
operation_one
operation_two
...
SUCCESS_STATUS_CODE
} if loaded_dependencies else {
do_detailed_logging
FAILURE_STATUS_CODE
}
if variable then
it_worked
else
explain_why
end
Because your example, while it seems readable to you, has too many ambiguities in other cases.
Not to mention that ruby has a way to do this, and it's the ternary operator. To say that your example is more ruby-like, is almost like wondering why the wheelbase of the Ford Mustang wasn't longer, and that it would be more "Mustang-like" if it was.
But here are some issues with your proposal, starting from your example:
variable = { 1 } if condition else { -1 }
Here you've given your "if else" bit a lower precedence than the "=".
In other words:
variable = ({ 1 } if condition else { -1 })
That's a serious problem, because it breaks the currently allowed:
variable = 1 if condition
The precedence for that statement is:
(variable = 1) if condition
And that's important. No assignment happens if the condition is false.
This can be a really big deal, for example if the lvalue (left side) actually has side-effects. For example:
var[0] = 1 if condition
The lookup for "var[0]" is a method in whatever class object var is, and while [] doesn't usually have side-effects, it can - and now you are going to do those side effects even if the condition is false.
And I won't even get into:
variable = { 1 } if condition if condition2 else { -1 }
But if you don't like it, you can always write your own language and see what happens!
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".
It's a bit confusing to me about what is the difference between these condition expressions below:
if( 1 == a) {
//something
}
and
if( a == 1 ) {
//something
}
I saw the above one in some scripts I have downloaded and I wonder what's the difference between them.
The former has been coined a Yoda Condition.
Using if(constant == variable) instead of if(variable == constant), like if(1 == a). Because it's like saying "if blue is the sky" or "if tall is the man".
The constant == variable syntax is often used to avoid mistyping == as =. It is, of course, often used without understanding also when you have constant == function_call_retuning_nothing_modifiable.
Other than that there's no difference, unless you have some weird operator override.
Many programming languages allow assignments like a = 1 to be used as expressions, making the following code syntactically valid (given that integers can be used in conditionals, such as in C or many scripting languages):
if (a = 1) {
// something
}
This is rarely desired, and can lead to unexpected behavior. If 1 == a is used, then this mistake cannot occur because 1 = a is not valid.
Well, I am not sure about the trick. Generally, we could say the equal sign is commutative. So, a = b implies b = a. However, when you have == or === this doesn't work in certain cases, for example when on the right side you have a range: 5 === (1..10) vs. (1..10) === 5.