Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: Go 2: add a ternary conditional operator #33171

Open
phrounz opened this issue Jul 18, 2019 · 66 comments

Comments

@phrounz
Copy link

commented Jul 18, 2019

I disagree with the Go convention and language's designers arguments here https://golang.org/doc/faq#Does_Go_have_a_ternary_form and think that it is a real missing feature in the language.

Consider in C the following code:

printf("my friend%s", (nbFriends>1?"s":""));

or in C++ :

std::cout << "my friend" << (nbFriends>1?"s":"") << std::endl;

In Go it causes either huges repetitions which can cause mistakes, or very verbose and inefficient code, or both, for something which should be straightforward:

Solution 1:

// horribly repetitive, risk of divergence between the two strings
if nbFriends > 1 { 
  fmt.Printf("my friends\n") 
} else { 
  fmt.Printf("my friend\n")
}

Solution 2:

// difficult to read
fmt.Printf("my friend")
if nbFriends > 1 { fmt.Printf("s") }
fmt.Printf("\n")

Solution 3:

// difficult to read
var plural = ""
if nbFriends > 1 { plural = "s" }
fmt.Printf("my friend%s\n", plural)

Solution 4:

// dangerous (ifTrue and ifFalse are both evaluated, 
// contrary to a real ternary operator), 
// and not generic at all (works only for strings)
func ifStr(condition bool, ifTrue string, ifFalse string) string {
  if condition { 
    return ifTrue
  }
  return ifFalse
}
fmt.Printf("my friend%s\n", ifStr(nbFriends > 1, "s", ""))

Solution 5:

// horrible to read, probably inefficient
fmt.Printf("my friend%s\n",
		func(condition bool) string {
			if condition {
				return "s"
			}
			return ""
		}(nbFriends > 1))

@bcmills bcmills changed the title Add a ternary form [Suggested labels: Go2 LanguageChange proposal] proposal: Add a ternary form Jul 18, 2019

@gopherbot gopherbot added this to the Proposal milestone Jul 18, 2019

@gopherbot gopherbot added the Proposal label Jul 18, 2019

@bcmills bcmills added Go2 and removed Proposal Go2 labels Jul 18, 2019

@bcmills

This comment has been minimized.

Copy link
Member

commented Jul 18, 2019

@gopherbot, add label Go2, LanguageChange

@ianlancetaylor ianlancetaylor changed the title proposal: Add a ternary form proposal: Go 2: add a ternary conditional operator Jul 18, 2019

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Jul 18, 2019

See also #31659 and #32860.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Jul 18, 2019

This decision has already been enshrined in a FAQ, as you note. If you want to argue that the FAQ answer should be changed, you need more than a few examples. I promise you that we've seen and considered those examples already. What you need is data: real programs with code that would become simpler and easier to read by adding a conditional operator. You also need arguments against the common concerns about the conditional operator, such as that it makes code harder to read especially when nested.

Also, a minor point, but your example is not great since it only works for English, and does not support localization of message strings.

@alanfo

This comment has been minimized.

Copy link

commented Jul 18, 2019

@ianlancetaylor

In #31659 you made what I thought was a very good counter-suggestion of having a built-in cond function to provide ternary functionality. This needed to be a built-in (as opposed to a generic function) to enable short-circuiting evaluation of the true/false arguments. It still suffered from the possibility that people could nest cond functions though personally I didn't regard that as a fatal problem because, even if they did, it should still be more readable than the hieroglyphics of C's ternary operator itself.

As that proposal has now been closed, do you intend to pursue that suggestion further or have you given up on the idea of having an alternative ternary form altogether?

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Jul 18, 2019

I don't personally intend to push that idea further. That was more of a discussion thought than a serious suggestion. Of course, I don't mind if someone wants to polish it into a real proposal. But in order to be accepted I think we would still need to see some data as to how much it would simplify real existing programs.

@alanfo

This comment has been minimized.

Copy link

commented Jul 18, 2019

OK, thanks for clarifying.

One has only to look at code in other C family languages to see how common the ternary operator is but it would be difficult to analyze Go code itself since, as @phrounz pointed out in his opening post, a number of constructions are used to work around its absence.

Using the cond idea, his example would become:

fmt.Printf("my friend%s\n", cond(nbFriends > 1, "s", ""))

Having said all that, if we get generics, I'd personally be content to write my own cond function and, given the lack of short-circuiting, only use it where the arguments were cheap to evaluate.

@DongchengWang

This comment has been minimized.

Copy link

commented Jul 20, 2019

In my opinion, to write more code (just a few lines) is better than to figure out the rule of x ? a : b. The if statement may seems verbose (not sure) but easy to understand.

Besides, a ternary conditional operator can be easily abused when people write multiple nested x ? a : b. The benefit of introducing it isn't great enough.

@Marcial1234

This comment has been minimized.

Copy link

commented Jul 20, 2019

I see ternary operators only being easy to visualize in one-liners functions. Even then, most of the time they deal with error handling, at which case is better to adhere to a "the less indentation the better" approach by having the error or least probable path for a function be wrapped in an if and handled then, as opposed to having multiple branching logic.

@Terottaja

This comment has been minimized.

Copy link

commented Jul 20, 2019

I think the example is kind of a bad one considering its only about one character and is not really readable in my opinion (?:"s":"")
but i do agree that the ternary operator should be added its a no-brainer for me
and i made up some examples which would be useful for me personally and i think you can somewhat relate to some of them.

const PORT = production ? 80 : 8080
instead of

const PORT = -1
if production {
    PORT = 80
else {
    PORT = 8080
}

fmt.Printf("Running %s build!", production ? "Production" : "Debug") .. etc

But of course the ternary operator is very hard to learn.

@alanfo

This comment has been minimized.

Copy link

commented Jul 20, 2019

Yes, that's a good example particularly if it's at top-level:

const production = true

//...

const PORT = production ? 80 : 8080

as you don't then need an init function to initialize PORT.

A built-in cond function might be able to be used as a const initializer though a generic version definitely could not.

@DisposaBoy

This comment has been minimized.

Copy link

commented Jul 20, 2019

@Terottaja @alanfo

I think the idiomatic solution to that specific problem is to use Build Constraints.

@DisposaBoy

This comment has been minimized.

Copy link

commented Jul 20, 2019

@Terottaja

See my other comment for the solution for global variables/constants.

For local variables/constants, I think the idiomatic way to write this peace of code:

const PORT = -1
if production {
	PORT = 80
else {
	PORT = 8080
}

is:

PORT := 8080
if production {
	PORT = 80
}

You could argue that I changed the const to a var, but I'd be surprised if the compiler wasn't smart enough ™ figure out that PORT is constant so IMO it makes no difference in real code.

@alanfo

This comment has been minimized.

Copy link

commented Jul 20, 2019

Although it probably wouldn't matter in this particular example whether PORT was a const or a var, more generally it might matter. For example, if you were declaring some integer which was to be used to define the size of an array.

Perhaps I should make my own position clear on this proposal. Whilst I personally have no problem with the 'standard' ternary operator, I'm not sure it would be a good idea to introduce it into Go in that form. I'd much prefer a cond built-in instead which is far more readable though, realistically, I see little chance of either being adopted.

@Terottaja

This comment has been minimized.

Copy link

commented Jul 21, 2019

@Terottaja

See my other comment for the solution for global variables/constants.

For local variables/constants, I think the idiomatic way to write this peace of code:

const PORT = -1
if production {
	PORT = 80
else {
	PORT = 8080
}

is:

PORT := 8080
if production {
	PORT = 80
}

You could argue that I changed the const to a var, but I'd be surprised if the compiler wasn't smart enough ™ figure out that PORT is constant so IMO it makes no difference in real code.

im just talking about if you have a lot of this kind of stuff in your code the ternary operator definitely would be more clean in this case, just my opinion

@Terottaja

This comment has been minimized.

Copy link

commented Jul 21, 2019

In my opinion, to write more code (just a few lines) is better than to figure out the rule of x ? a : b. The if statement may seems verbose (not sure) but easy to understand.

Besides, a ternary conditional operator can be easily abused when people write multiple nested x ? a : b. The benefit of introducing it isn't great enough.

there will always be people abusing it, code can be abused its inevitable but will that be affecting you? in most cases, no

@Lexkane

This comment has been minimized.

Copy link

commented Jul 28, 2019

I support this feature, while it can lead to abuses in code, it is really beneficial to compiler optimisations, when u avoid generic if /else and replace it with ternary operator.
People have been doing bitwise shifts from the start (who can blame them, and still no one suggests let's take away bitwise shifts cause of readability), and ternary operator is just crucial to have nowdays.

@randall77

This comment has been minimized.

Copy link
Contributor

commented Jul 28, 2019

@Lexkane: The compiler already has optimizations that use conditional moves. We don't need a language construct to force such optimizations. For instance, the following code uses one:

func f(x, y int) int {
	r := 3
	if x < y {
		r = 7
	}
	return r
}

If you have particular instances where a conditional move is not being generated, and you think it should, open an issue with the code.

@DmitriyVTitov

This comment has been minimized.

Copy link

commented Aug 22, 2019

Since I use Go and Javascript in my job at the same time it has been countless times I wanted to write x ? a : b in Go programs! I should have written it down to show all theese cases to @ianlancetaylor ! It all was real programs.
Ternary operator is the one we all learn at school (not even at University) so in it's classic form it is the natural way to write and read code.
We all learned that there are three types of operators: unary, binary and ternary. Go lacks one type for no real reason IMO.
Both hands fo x ? a : b.

@DeedleFake

This comment has been minimized.

Copy link

commented Aug 22, 2019

We all learned that there are three types of operators: unary, binary and ternary. Go lacks one type for no real reason IMO.

There's no reason that there has to be, though. 'ternary' is just an English word that means 'composed of four parts'. You could just as easily have quaternary or quinary operators, too.

Personally, I feel that ternary operators default to being annoying to read. With unary and binary operators, you can easily see exactly where everything is, but with ternary it's not always clear what goes with what, especially once you start nesting them. I can see the argument for them being cleaner in specific situations, but outside of those situations they're almost always worse. gofmt could help, potentially, but only if it was a lot more aggressive about how it reformatted code than it is. Maybe some kind of limited one could be introduced, like disallowing nesting or chaining it, but at that point I'm not sure if it's really worth it.

@DmitriyVTitov

This comment has been minimized.

Copy link

commented Aug 22, 2019

It has been already said that one can make the mess with the simplest set of operators. It's true that Go-code is simpler to read and write than Java or Javascript code. But it's not impossible to make it unreadable.
So ternary operator is for one-liners mostly and should be used for those cases.
Otherwise you can take "if - then - else", nest it several times and make your code a total mess. It's always up to you.
I think that people underestimate the frequency one-liner expressions are arise in the code. Sometimes they are rare but sometimes they fill half of the code written and in later case i want to have ternary operator preferably in the form it is in Javascript.

@lukechampine

This comment has been minimized.

Copy link
Contributor

commented Aug 22, 2019

I like that in Go, control flow is generally done with statements, not expressions (function invocation being the obvious exception). Many popular languages strive for "everything is an expression," which is often fun, but imo encourages writing "clever" code. Go, to me, is all about minimizing cleverness.

By the way, another (gross) way to implement a ternary expression is:

map[bool]string{true: "", false: "s"}[nbFriends == 1]
@mdaliyan

This comment has been minimized.

Copy link

commented Aug 25, 2019

What I like about Go so far is the fact that it lessens the argument between team members about the way of coding, about the code style each of them may use (remember various versions of PSRs in PHP?). We, at our 7-member team, never argue about each other's codes. We just focus on our goal.

Not that i don't like ternary conditional operator, but I need to object adding it and anything such to Go because I don't like the way of coding become one of our arguments.

@Didainius

This comment has been minimized.

Copy link

commented Aug 26, 2019

Unneeded sugar. I think it is one of those times when we're trying to bring something that "was used in another language". Doesn't feel Go.

@ianlancetaylor

This comment has been minimized.

Copy link
Contributor

commented Aug 27, 2019

As noted above, there is an existing FAQ entry explaining why the language does not have the conditional operator. The issue does not have strong support. Adding a new feature to the language can never make it simpler. Once there are two ways of doing something (if or ?:), each programmer will often have to decide which form to use, which is not a good thing. In general, we don't see any new arguments here.

Therefore, this is a likely decline. Leaving open for a month for final comments.

@themartorana

This comment has been minimized.

Copy link

commented Aug 27, 2019

To @ziflex 's point, I'm not sure how a verbose inline if:

if bool := operation(); bool {}

is much different from a ternary operator. It's a construct I wouldn't happily give up, but it has the possibility of suffering the same complexity of an eval ? a : b like:

func main() {
	if a := A(); !B(a) && !C(a) && D(C(a)) {
		fmt.Println("confused")
	}
}

func A() bool {
	return true
}

func B(in bool) bool {
	return !in
}

func C(in bool) bool {
	return in
}

func D(in bool) bool {
	return in
}

This serves only to illustrate that poorly written code on a single line can become as unreadable as some of the examples above. The ability to abuse a syntax, IMHO, shouldn't be reason enough to prevent a significantly usable operator from being added to the language.

@phrounz

This comment has been minimized.

Copy link
Author

commented Aug 28, 2019

However, if-else form does its job. I think a shorter form is not always the clearer form.

var a, b, c int
fmt.Println("Example bool is: ", a != 0 && b != 0 ? c != 0 ? "true" : "false" : "false")

That's a bad example, because you omitted parenthesis, which makes your example sound extremely obfuscated while it could be understandable.

I genuinely think

var a, b, c int
fmt.Println("Example bool is: ", (a != 0 && b != 0 ? (c != 0 ? "true" : "false") : "false"))

is more readable than

var a, b, c int
var d string
if a != 0 && b != 0 {
        if c != 0 {
                d = "true"
        } else {
                d = "false"
        }
} else {
        d = "false"
}
fmt.Println("Example bool is: ", d)

(Even if your example could actually be even simpler, but I assume that's not the point: )

fmt.Println("Example bool is: ", (a != 0 && b != 0 && c != 0 ? "true" : "false"))
@seebs

This comment has been minimized.

Copy link
Contributor

commented Aug 28, 2019

Although I occasionally find myself wanting a conditional within other expressions or statements, I nearly always feel like it would be a net loss to readability.

The one thing I do regret about it is the lack of a way to have a conditional initialization where both branches would not be a zero value. I know the compiler is probably smart enough to make this not wasteful, but my sense of what the abstract-machine does tells me that I'm zeroing a value and then overwriting it immediately. This is... a pretty weak argument, really.

FWIW, I don't find the ternary form of the "Example bool" examples more readable, not least because, on my display right now, they end up requiring horizontal scrolling to even see the full expression. Even doing that, I have to look back and forth a lot more often to figure out what it's doing.

The obvious answer from some of the scripting languages is to have if statements be expressions that can be assigned, and I actually really like that design for those languages, but I do not think I would like it so much for go.

I suppose there's always:

x := func() int {
    if a {
        return 1
    }
    return 2
}()

... but come to think of it, if we could streamline that, it would actually be pretty usable for circumstances like this. Something which lets us express "this function is actually merely notational, there is no need to actually generate function code, we just want a way to use return expressions in an inner block"...

@Yanwenjiepy

This comment has been minimized.

Copy link

commented Aug 28, 2019

The Go language is born for simplicity. Why do you want to do these fancy things? You feel that you have written a few lines of code, but it adds more complexity and confusion to the code.
So I don't support

@nkev

This comment has been minimized.

Copy link

commented Aug 29, 2019

It’s not “fancy”, it’s “simple” so it fits right in. It’s familiar because we use it in many other languages. It’s convenient because it’s a single line expression with little typing. It’s important to have because it’s a common construct we would use a lot.

@Yanwenjiepy

This comment has been minimized.

Copy link

commented Aug 29, 2019

Then I would rather use a list comprehension like python:
a if a>1 else b
Instead of all sorts of strange symbols, just like Rust.
I would rather write more code to express it than to use these strange symbols to omit the code.
The code is for people to read.

@Yanwenjiepy

This comment has been minimized.

Copy link

commented Aug 29, 2019

Please don't add new grammar functions for a situation that is not commonly used, because you are destroying Go's original intention, that is, simple and easy to read.
I think that sometimes, a lot of people are selfish, for their convenience in certain situations, or because they are spoiled by the convenience features provided by other programming languages, you have to add their favorite features in Go.
What I want to say is that Go is not your language, Go has his own way to go.
Although you can invent your own language, you can.

@nkev

This comment has been minimized.

Copy link

commented Aug 29, 2019

@Yanwenjiepy Looks like you've also become a Go "purist" that in my opinion is hindering progress.

I would rather write more code to express it than to use these strange symbols
It may be strange to you but not strange symbols for most of us who are familiar with any common C based language; C, C++, C#, Java, JavaScript, etc. They all have the ternary expression.

Please don't add new grammar functions for a situation that is not commonly used
Ternary conditional statements are actually very handy and commonly used!

@seebs

This comment has been minimized.

Copy link
Contributor

commented Aug 29, 2019

Just to be picky, that's not a "list comprehension". A list comprehension is specifically something like [x for y in z] (possibly plus conditions). The use of if/else in expressions is a different feature.

I'm plenty familiar with ternary operators, and have used languages that had them for most of my life, but roughly 95% of the usages I have seen in other languages, I think would be a poor fit for what makes Go pleasant to work in. Go has tended to avoid information density of some kinds, like preincrement/postincrement operators that can be used in expressions, and I think the ternary operator has the same underlying problem; it takes too much space to think through what it does.

The compiler is reasonably smart. You can declare a value, assign to it conditionally, use it once, and expect the compiler to do about as well as it would have for a ternary expression.

@Yanwenjiepy

This comment has been minimized.

Copy link

commented Aug 29, 2019

@Yanwenjiepy看起来你也成了一个Go“纯粹主义者”,在我看来这阻碍了进步。

I would rather write more code to express it than to use these strange symbols
对于我们大多数熟悉任何常见C语言的人来说,这可能是奇怪的,但不是奇怪的符号; C,C ++,C#,Java,JavaScript等。它们都具有三元表达式。

Please don't add new grammar functions for a situation that is not commonly used
三元条件语句实际上非常方便且常用!
Maybe, I have a similar feeling myself. Perhaps on other issues, I am not a ‘Go purist ‘’. This is very confusing.

@nkev

This comment has been minimized.

Copy link

commented Aug 29, 2019

@seebs I can certainly respect that opinion, but for a lot of us coming from other C-based languages, "pleasant to work in" means productivity via familiarity and convenience. I could never comprehend why i++ in Go is less pleasant/convenient than i = i + 1, especially when a postincrement is ok in loops, e.g. for i := 0; i < 5; i++ {...} but it's not OK as a statement! Too much purism I say! :)

@seebs

This comment has been minimized.

Copy link
Contributor

commented Aug 29, 2019

What are you talking about? i++ is perfectly allowable as a statement in Go. what's not allowed is using it in an expression, where it is evaluated both for a value and for a side effect.

https://play.golang.org/p/m_LbSbmT1Ar

As someone reasonably solidly familiar with C, I nonetheless find that I'm fine without the ternary operator, and I like the resulting code better. I've stopped using it as much in C, too, just like I've become more consistent about using braces on all blocks, not just blocks with more than one statement in them.

@nkev

This comment has been minimized.

Copy link

commented Aug 29, 2019

I didn't mean i++ on it's own, I meant as an expression like fmt.Printf("%d", i++) which would be a convenience for some of us.

@seebs

This comment has been minimized.

Copy link
Contributor

commented Aug 29, 2019

Yes, it's definitely convenient, but it is, pretty clearly, less maintainable. People make mistakes with it, people misunderstand code using it. It is a source of bugs, and it just plain doesn't add that much value.

Yes, if I want to do x++, I have to do it as its own statment before or after the Printf. That is indeed a cost, at all. But in exchange, I get:

  • I don't suddenly have x getting the wrong values if I comment out some of the Printf calls.
  • Changes to the format messages don't break my program logic.
  • Someone else skimming the code (or me without enough coffee) won't just plain miss that the increment is in there.

It is a tradeoff, but I think it's a pretty good one. I spend some of my time trying to answer newbie programming questions, because that's part of how we develop healthier language communities. I spend a lot less time trying to puzzle through subtleties in punctuation in people's Go code than I do in their C code, and they have a lot fewer problems caused entirely by subtle typos.

I assure you, this isn't purism coming from people who don't understand or appreciate C. It's a considered decision that this language seems to get a lot of value from simply not being that complicated, and that leaves us more room to be doing complicated things with our logic since we're not spending so much of our effort parsing the code.

@nkev

This comment has been minimized.

Copy link

commented Aug 29, 2019

I hear you, but I'm not convinced on all of that. For example, the following works in Go and according to your argument, it should be the only allowed syntax:

	for i:=0; i<5; i=i+1 {
	  fmt.Printf("%d\n", i)
	}

But the more popular/familiar syntax IS also allowed:

	for i:=0; i<5; i++ {
	  fmt.Printf("%d\n", i)
	}

Why do you think that is?

@seebs

This comment has been minimized.

Copy link
Contributor

commented Aug 29, 2019

My argument does not in fact state that only the first should be allowed. I like the second better, it's simpler and easier to read, because the increment operator is a standalone thing, not a side-effect.

Note that you can't do:

i := 0
for i++ < 5 {
    ...
}

Because Go doesn't let you put assignments, or increments, in expressions. And that may be an inconvenience sometimes, but the frequency with which it does not result in people misunderstanding an expression and not realizing it modifies values is basically 100%, which is nice.

@nkev

This comment has been minimized.

Copy link

commented Aug 29, 2019

See that's too purist to me :) Anyway, my point is, since both i=i+1 and i++ are allowed in loops, I say also allow a ternary variation for those who prefer the single-line convenience, e.g.

Port := production ? 80 : 8080

as well as the usual:

Port := 8080
if production {
	Port = 80
}

If it's about simplicity, I think the former is simpler.

@seebs

This comment has been minimized.

Copy link
Contributor

commented Aug 29, 2019

What about:

Port := production++

or

fmt.Printf("port: %d\n", production++)

Both of those are also shorter using the C idiom than they would be in Go. But I would argue that in both cases, the possibility of that complexity existing makes the entire program slightly harder to understand -- you now have to be watching out for those effects all the time.

At a more fundamental philosophical level: The problem is that your solution is not only for those who prefer that "convenience". It's also imposed by force on everyone else, forever. Because everyone has to read other people's code. So people can't opt-out of a feature like this. They can't say "well, that's harder for me to maintain, so I won't use it" and not have to deal with it. They're stuck with it being part of their world. Whenever they read code that anyone else could have worked on, they have to watch out for a new set of complexities.

In practice, this "simplicity" comes at a significant cost, because it's not actually simplicity, it's just making the expression shorter. It's like the single-line braceless if; it seems like it's simpler, but then you need a second line and there's a non-zero chance that you forget to add the braces then, and pretty soon you've lost more time than you would have just putting the braces there.

I know what happens if you write:

Port := production ? 80 : 8080

A few days later:

Port := production ? 80 : test ? 4080 : 8080

But then someone realizes that the two bools are a bad choice, and fixes that:

Port := mode == "production" ? 80 : mode == "test" ? 4080 : 8080

and because it was just one line, and using ?:, people feel like making it longer is Extra Effort, and they don't fix it or clean it up. And now they've got an investment in it being that way.

And that's how you end up with ?: operations nested 15 deep, which I've seen in actual code, which should absolutely have been lookup tables.

@phrounz

This comment has been minimized.

Copy link
Author

commented Aug 29, 2019

And that's how you end up with ?: operations nested 15 deep, which I've seen in actual code, which should absolutely have been lookup tables.

"if-else" operations nested 15 deep, should also absolutely have been lookup tables, though.

@seebs

This comment has been minimized.

Copy link
Contributor

commented Aug 29, 2019

Oh, certainly.

But if you have 15-deep nested if/else operations, and you convert to a lookup table, you don't feel like you've lost the "simplicity" of the single-line solution.

@nkev

This comment has been minimized.

Copy link

commented Aug 30, 2019

The problem is that your solution is not only for those who prefer that "convenience". It's also imposed by force on everyone else, forever

I couldn't disagree more because the truth is the opposite! It is actually your purist view that imposes your way of doing it and limits my freedom to choose a short-hand version. If you don't want to use it, that's your choice, but don't limit my choice!

If I can choose to write a shortened a := "freedom" instead of var a string = "freedom" then I should have the freedom and convenience of a ternary assignment.

Go tooling does a great job of standardising code formatting and I think that alone makes it quite easy to read other people's code.

The bottom line for me is, I find ternary assignments easier to read and understand because they are more naturally translated to English. I believe this is why it is so popular in many other languages. To me this:

port := production ? 80 : 8080

...translates to : "Is this production? if yes, port is 80 and if no, port is 8080"
(a simple, straight forward single assignment, even when nested)

port := 8080
if production {
	port = 80
}

This translates to: "port is 8080 (period) Oh, but if this is production, change port to 80" (second assignment).

The second is definitely NOT easier to read for me. Never has been.

If it's the ? and : that is bothering people, I'd also be happy with any alternative single line syntax.

@networkimprov

This comment has been minimized.

Copy link

commented Sep 3, 2019

This single-line construct works for non-computed initial values. A way to extend this to computed initial values would be great.

v := a; if t { v = b }        // non-computed initial value

v := f(); else if t { v = b } // f() not evaluated where t==true

Sadly go fmt destroys many useful single-line constructs. I don't use go fmt for that reason; telescoping readable compact code makes it less readable. But that's tangential.

@seaskyways

This comment has been minimized.

Copy link

commented Sep 14, 2019

This functionality is achievable without a ternary operator if Go 2 adds Generics

func ternary(type T)(cond bool, vTrue, vFalse T) T { 
    if cond { return vTrue } else { return vFalse }
}

Of course using this wouldn't be pretty especially if there was more than one ternary call.

As for evaluation, this may be an optimization on the compiler level.

@phrounz

This comment has been minimized.

Copy link
Author

commented Sep 15, 2019

This functionality is achievable without a ternary operator if Go 2 adds Generics

func ternary(type T)(cond bool, vTrue, vFalse T) T { 
    if cond { return vTrue } else { return vFalse }
}

Of course using this wouldn't be pretty especially if there was more than one ternary call.

As for evaluation, this may be an optimization on the compiler level.

Hum no, vTrue and vFalse will always be evaluated, I mean

ternary(3>2, func1(), func2())

will cause the call of both func1() and func2(). No compiler could know that func2() do not need to be evaluated... and it should never assume that anyway, because it's a fundamental principle that in a function call the arguments are always expected to be evaluated, before the call of the function itself. If func2() does stuff additionally to returning a value, we want this stuff done, otherwise it would be very unpredictable and difficult to comprehend.

(Contrary to a real ternary operator where the value of false is not supposed to be evaluated by principle.)

@seaskyways

This comment has been minimized.

Copy link

commented Sep 16, 2019

This functionality is achievable without a ternary operator if Go 2 adds Generics

func ternary(type T)(cond bool, vTrue, vFalse T) T { 
    if cond { return vTrue } else { return vFalse }
}

Of course using this wouldn't be pretty especially if there was more than one ternary call.
As for evaluation, this may be an optimization on the compiler level.

Hum no, vTrue and vFalse will always be evaluated, I mean

ternary(3>2, func1(), func2())

will cause the call of both func1() and func2(). No compiler could know that func2() do not need to be evaluated... and it should never assume that anyway, because it's a fundamental principle that in a function call the arguments are always expected to be evaluated, before the call of the function itself. If func2() does stuff additionally to returning a value, we want this stuff done, otherwise it would be very unpredictable and difficult to comprehend.

(Contrary to a real ternary operator where the value of false is not supposed to be evaluated by principle.)

Then the signature would be like so:

func ternary(type T)(cond bool, vTrueFunc, vFalseFunc func() T) T { 
    if cond { return vTrueFunc() } else { return vFalseFunc() }
}

I gotta admit though, this implementation is a whole lot ugly :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
You can’t perform that action at this time.