Why is there no ternary operator detail in golang

  • 2020-11-03 22:24:04
  • OfStack

The 3-element operator is widely used in other languages, such as:

python:


val = trueValue if expr else falseValue

javascript:


const val = expr ? trueValue : falseValue

c, c + + :


const char *val = expr ? "trueValue" : "falseValue";

However, the widely supported 3 - mesh operator does not exist in golang! If we write something like this:


val := expr ? "trueValue" : "falseValue"

Then the compiler should complain: invalid character U+003F '? '. It means golang doesn't exist ? This operator, which is not recognized by the compiler and cannot be used as a variable name, is considered illegal.

But why? Actually, the official explanation is given. Here's a simple quote:

[

The reason ?: is absent from Go is that the language's designers had seen the operation used too often to create impenetrably complex expressions. The if-else form, although longer, is unquestionably clearer. A language needs only one conditional control flow construct.

][

Not in golang ? The reason for the operator is that the language designer foresaw that 3-element operators would often be used to construct extremely complex expressions. While using if instead makes the code look longer, it's certainly more readable. A language only needs one condition to determine its structure.

]

There is no doubt that this is the product of golang's principle of "simplicity".

This is fine, because some scenarios for using 3-element operators do make the code less readable:


const status = (type===1?(agagin===1?' resale ':' sold '):' unsold ')

const word = (res.distance === 0) ? 'a'
  : (res.distance === 1 && res.difference > 3) ? 'b'
  : (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) ? 'c'
  : 'd';

At first glance, this is really complicated, and at least the second expression may not be able to untangle the flow of control without spending 20 seconds looking at it (imagine indenting incorrectly or without indenting at all).

If you translate them directly into if statements, it looks like this:


let status = ''
if (type === 1) {
  if (again === 1) {
    status = ' resale '
  } else {
    status = ' sold '
  }
} else {
  status = ' unsold '
}

let word = ''
if (res.distance === 0) {
  word = 'a'
} else {
  if (res.distance === 1 && res.difference > 3) {
    word = 'b'
  } else {
    if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) {
      word = 'c'
    } else {
      word = 'd'
    }
  }
}

It doesn't seem like much of an improvement, especially with example 2 and 3 levels of nesting, so whoever review goes into this code won't complain about it.

In fact, however, this code can be simplified. Considering that the 3 element operator always gives a value to a variable, else can be regarded as the default value of the variable. The code can be written as follows:


let status = ' unsold '
if (type === 1) {
  if (again === 1) {
    status = ' resale '
  } else {
    status = ' sold '
  }
}

let word = 'd'
if (res.distance === 0) {
  word = 'a'
} else {
  if (res.distance === 1 && res.difference > 3) {
    word = 'b'
  } else {
    if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) {
      word = 'c'
    }
  }
}

Second, for example 2, it is obvious to use else if to clear the nested structure:


let word = 'd'
if (res.distance === 0) {
  word = 'a'
} else if (res.distance === 1 && res.difference > 3) {
  word = 'b'
} else if (res.distance === 2 && res.difference > 5 && String(res.key).length > 5) {
  word = 'c'
}

Now, it is clear that the version using if statements is more readable and logical (by removing nesting).

But that is not the case. In addition to using the 3-element operator to express process control, in fact a more common and widespread application is an expression like this:


const val = expr ? trueValue : falseValue

const func = (age) => age > 18 ? ' adults ' : ' minors '

Similar to the above, using a short conditional expression to determine the value of a variable occurs quite frequently in development. The intent of the 3-element operator is clearer and more readable than the if statement, especially when used in conjunction with anonymous functions (lambda expressions) to greatly simplify our code.

The solution to python is to support the simplified version of the above 3-element expression, and the expression does not support nesting, to achieve the purpose of exploiting advantages and avoiding disadvantages. However, this comes at the expense of the compiler's associated implementation.

For golang, a simple compiler that can complete an ast build with a single scan is one of its secrets for fast build speeds, and adding complexity to the compiler implementation for such a simple feature is unacceptable. At the same time, due to golang's philosophy of "simplicity", problems that can be solved with existing grammatical structures will not be added with new grammars.

But there are ways, though not recommended:


func If(cond bool, a, b interface{}) {
  if cond {
    return a
  }

  return b
}

age := 20
val := If(age > 18, " adults ", " minors ").(string)

This is not recommended for several reasons:

Using interfaces results in performance degradation Type assertions that need to be enforced Regardless of the 3-element expression or if statement, it is not evaluated for branches that cannot be reached, that is, lazy evaluation; Every expression is evaluated when passing arguments to a function

In conclusion, the following is the following:

Advantages of the 3-element operator:

Using 3-element operators for short expressions is much clearer, especially since you are used to reading 3-element operator expressions in a linear fashion No intermediate states are required (let variable in example 1 can be replaced with const for more robust code) and the mental burden is lower No intermediate state means fewer or no side effects, and the code is easier to track and maintain

But the 3-element operator also has obvious drawbacks:

For complex logic code, the readability is poor (for example, status in the first example, when you need to make further conditional judgments at the location of trueValue) It is easily abused and many people use it to replace if statements or to simplify complex if nesting, which can lead to the results described in the previous article Conditional branches can only be expressions and do not support multiple statements

So this is a matter of opinion, in short, we have to do as the Romans do.

reference

https://juejin.im/post/6844903561759850510

https: / / www. it - swarm. dev zh/javascript/replacement js nested in the operator / 1055944752/3 yuan

https://golang.org/doc/faq#Does_Go_have_a_ternary_form

conclusion


Related articles: