err := h(cond(x) ? f(x) : g(x))
vs var temp T
if cond(x) {
temp = f(x)
} else {
temp = g(x)
}
err := h(temp)
Hopefully, this can be improved with generics, e.g. now it should be possible to create e.g. `func Coalesce (type T) (...args T) T` function to simplify common scenarios.Ken Thompson added ?: to B / C and then he took it from us in Go due to the wisdom he gathered in between.
And yet, Go has a switch statement.
var someVar, anotherVar string
// ...
switch {
case someVar == "whatever":
fmt.Println("Tell me how, exactly,")
case anotherVar == "nope":
fmt.Println("this compiles to a jump table?")
default:
fmt.Println("Spoiler: it doesn't.")
}If the switch is over a fixed set of strings, then can't the backend generate a perfect hash function ala gperf and then proceed to use the computed hash value to implement a jump table?
Also, FWIW, the only compiler backends I've ever seen blindly emit a jump table all died in the '80s. Jump tables aren't always the fastest choice: https://www.cipht.net/2017/10/03/are-jump-tables-always-fast...
Note that my example “switches” over two independent variables. Even if the Go compiler did generate a table, for this example it would really be generating two tables: and then we're back to the “how is this better than an `if`?” question.
I disagree:
- The variable name is repeated three times, increasing visual clutter and the chance of typos.
- The variable can't declared immutable. In Java/JS/Dart/etc it's nice to know that after "const x = foo ? bar : baz", x won't change.
And you don't even have to add a new operator, Rust and Kotlin get these benefits with an expression form of if/else.
I can't find that code but IIRC it was me working with with AWS SDK and it infamously exposes all sort of stuff as *strings and *ints because there is no sane way to have Maybe<string> in Go. So I had a whole screen of those `if resp.foo != nil { v = *resp.foo } else { v = fallback }`, sometimes deeply nested (for resp.foo.bar[idx].baz, because no null chaining operator either). Yes, a purist may argue that's AWS SDK problem and they should've went with something else, and that Go's decision to not have ?: still stands true. Sadly, I haven't had luxury to wait another decade until Amazon maybe sorts it out, yet I still wanted to write something I could read back.
I don't disagree typical use results in poor readability. I'm not gonna put those ?:s just about everywhere. Yet I do think that there are sometimes infrequent exceptions where ternary would've been objectively better.
err := h(cond(x) ? f(x) : g(x))
... then I'd much rather stick with the verbosity. There's way too much going on in a single line here.I'd rather have if statements be expressions, with gofmt-enforced line breaks:
err := h(
if cond(x) {
f(x)
} else {
g(x)
},
)
No unnecessary temp variables and no stuffing everything into one line.Reason is I prefer to move my eye balls than scroll the screen. Too many lines and you forget what preceded what you're seeing or have to keep scrolling to make sense of everything
foo := if a { b } else { c } err := h(map[bool]func(T){true: f, false: g}[cond(x)](x))
You're probably not going to want to use this though, although I've used the map[bool]T trick in some simpler examples. var temp func(x) T = g
if cond(x) {
temp = f
}
err := h(temp(x))