Ternary ... again

1,020 views
Skip to first unread message

Mark Volkmann

unread,
Aug 14, 2018, 12:43:37 PM8/14/18
to golan...@googlegroups.com
I’m new to Go and I imagine the idea of adding a ternary operator to Go has been discussed many times. Rather than repeat that, can someone point me to a discussion about why Go doesn’t add this? I’m struggling to understand why it is desirable to write code like this:

var color
if temperature > 100 {
color = “red”
} else {
color = “blue”
}

Instead of this:

var color = temperature > 100 ? “red” : “blue”

Is the ternary really so confusing that it justifies writing 6 lines of code instead of 1? I realize I could eliminate two lines like the following, but this isn’t a good idea if the values come from function calls since there would sometimes be needless function calls.

var color = “blue”
if temperature > 100 {
color = “red”
}

---
R. Mark Volkmann
Object Computing, Inc.

Axel Wagner

unread,
Aug 14, 2018, 1:19:31 PM8/14/18
to r.mark....@gmail.com, golang-nuts

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Agniva De Sarker

unread,
Aug 15, 2018, 12:52:51 AM8/15/18
to golang-nuts

Sam Vilain

unread,
Aug 15, 2018, 2:33:28 AM8/15/18
to Agniva De Sarker, golang-nuts
I haven't seen all the discussion referenced, but I remember digging deep into the python language archives where Guido and others eventually relented and added ternaries (with the syntax "a if val else b"). I can't remember the argument which swung the consensus but the arguments against seem remarkably similar.

Go does have a one-line ternary:

var result = map[bool]string{false: "a", true: "b"}[test]

It's less of a hack if you declare the lookup "table" separately.

Sam

--

Axel Wagner

unread,
Aug 15, 2018, 4:13:35 AM8/15/18
to golang-nuts
In my opinion Python serves as a poor argument here. I tend to use Python as a example of a grab-bag language that adds any feature anyone considers useful - without considering the cost. An Anti-Go, if you will :)
Gustavo Niemeyer actually wrote this up pretty well: https://blog.labix.org/2012/06/26/less-is-more-and-is-not-always-straightforward

So, from my perspective, if you tell me "Python did not have ternary operators, but after long and hard discussions, they caved", what I'm hearing is "even Python didn't really need them". ;)

(Disclaimer: This isn't meant as a dig at Python. I think Python is a great language. But its design goals are very different from Go's)

Mark Volkmann

unread,
Aug 15, 2018, 7:23:36 AM8/15/18
to Axel Wagner, golang-nuts
I realize I’m just another new person to Go asking for features that have been discussed many times before and that are very unlikely to be changed. But I’d like to consider a kind of middle ground that applies to many potential features.

I understand that many requests for syntax additions are rejected on two grounds. One is that it is possible to abuse the feature, resulting in code that is more difficult to understand. Another is that it makes the language specification larger, adding to the list of things that new Go developers must learn.

I see many proposals that would reduce the amount of code to be typed rejected on the basis that doing that alone is not a valid reason to add a feature. For things that would not be used often, I agree with that. But I want to discuss two features that I use often in other languages.

The first is ternaries. What if only simple, non-nested ternaries were supported? For example, color := temperature > 100 ? “red” : “blue”. This seems so much more clear than the map[bool]:string trick that some have proposed. Writing this with an if statement takes either 4 or 6 lines. This comes up often enough in code to make a significant difference. I think it’s hard to argue that this has the potential to make code harder to read if they can’t be nested.

The second is arrow functions. What if only functions that return the result of a single expression were supported? For example, n => n * 2. Compare this to func (n int) { return n * 2 }. The value of this is closely tied to whether generics are added to the language at some point.

Adding just a few things like these to the language might help with adoption and that in itself is a worthwhile goal. I suspect many developers that love Go aren’t currently using it on real projects. Getting more developers to consider Go makes that more likely.


---
R. Mark Volkmann
Object Computing, Inc.

peterGo

unread,
Aug 15, 2018, 7:44:18 AM8/15/18
to golang-nuts
Mark,

"Adding just a few things like these to the language might help with adoption and that in itself is a worthwhile goal. I suspect many developers that love Go aren’t currently using it on real projects. Getting more developers to consider Go makes that more likely."

There are several fallacies in your arguments (opinions,  suspicions). Powerful simplicity is a great virtue. Bjarne Stroustrup (C++) provides some counter arguments to the proliferation of language features.


Peter

Axel Wagner

unread,
Aug 15, 2018, 7:46:17 AM8/15/18
to Mark Volkmann, golang-nuts
Hi,

On Wed, Aug 15, 2018 at 1:23 PM Mark Volkmann <r.mark....@gmail.com> wrote:
 I understand that many requests for syntax additions are rejected on two grounds.

Really though, the reaction always happens on the same grounds: It is not shown that the benefit outweighs the cost. You mention the cost in your message and try to reduce that (which is fair). But even if I agree that the cost is relatively minor, I still also disagree about the benefit. It seems very small too.
 
The first is ternaries. What if only simple, non-nested ternaries were supported? For example, color := temperature > 100 ? “red” : “blue”.

Personally, I'm worried about the implications this has on the second cost you mentioned: Size of the spec. It's actually pretty simple to specify the grammar in a way to support general ternary conditional expressions. It's harder to do that with this limitation.
 
This seems so much more clear than the map[bool]:string trick that some have proposed.

To be clear: That is not the alternative you should be using. You should be using if-statements. And I disagree that a ternary conditional expression is significantly (if at all) clearer than a conditional statement.

Writing this with an if statement takes either 4 or 6 lines.

LOC is not a useful primary measure of clarity or readability. For example, list-comprehensions could definitely reduce the LOC in Go code, but I'd argue that they reduce readability.

This comes up often enough in code to make a significant difference. I think it’s hard to argue that this has the potential to make code harder to read if they can’t be nested.

I think this is an inversion of the argument. The default decision for language changes is "reject", so the onus is on the proponents of a feature to argue that it significantly improves on the design goals of Go - most significantly, to enable large-scale software engineering.
One way to do that, would be to try and make an argument that it makes code easier to read. Personally, I think that's a hard argument to make, given how subjective something like "readability" is and that the designers of the language have already explicitly said that they don't think it does.

Volker Dobler

unread,
Aug 15, 2018, 8:49:37 AM8/15/18
to golang-nuts
On Wednesday, 15 August 2018 13:23:36 UTC+2, Mark Volkmann wrote:
[...]
The first is ternaries. What if only simple, non-nested ternaries were supported? For example, color := temperature > 100 ? “red” : “blue”.

It is easy to write "non-nested" but it is hard to define.
Probably a?(b?c:d):e is "nested" but:
Is f(a?b:c) a nesting of ?:
Is g(a?b:c, d?:e:f) nested?
It gets ugly very fast.

And: Your example looks innocent, but requirement never
stay innocent. In 6 month you will have to encode 3 temperature
ranges in red, green and blue and you will have to replace the
?: with if/else if/else or a switch anyway. So why not start out
directly with an if?
 
[...]

I personally doubt that adoption per se is a goal. And if than
Go's adoption is perhaps driven by actual absence of this
syntactical sugar. Especially complicated syntactical sugar like
ternary ?: is fine but not nested!

V.
Message has been deleted

Hoo Luu

unread,
Aug 15, 2018, 10:46:51 AM8/15/18
to golang-nuts
在 2018年8月15日星期三 UTC+8上午12:43:37,Mark Volkmann写道:
var color = temperature > 100 ? “red” : “blue”  
 
Although this feature will not be accepted, we could just talk about it. I prefer 'if-expression'  to ternary.

var color = if temperature > 100 { "red" } else { "blue" }

vs. current if statement syntax:
Message has been deleted

Tamás Gulácsi

unread,
Aug 15, 2018, 11:06:48 AM8/15/18
to golang-nuts
I prefer with nice clean unmissable "default vs. special"

color := "blue"

Michael Jones

unread,
Aug 15, 2018, 12:00:49 PM8/15/18
to Tamás Gulácsi, golang-nuts
Mark,

Some considerations that may be unclear when approaching Go and considering anything familiar from other languages that is not in Go...

MAKING DECISIONS

Go's designers are experienced. with central roles in UNIX, C, GCC, Modula, etc., and also across teams of various scales and diversities, from small-all PhD Bell Labs teams to big more normally distributed background teams in large companies. They all ended up at Google and saw the "horror" of programming as usual at huge scale: many hour builds even with 10k CPUs, long latent bugs despite testing and review regimentation, etc.

The result was a project to design a language and build approach that makes it hard to abuse, stumble, or fail in precisely the ways that teams commonly suffer problems. This is a little bit downplayed in the conversations, but statements like "complex ternary expressions can be confusing" really mean "we documented 18,327 cases of major bugs caused by misunderstood nested ternaries." (This would also correspond to "we documented 9M cases of bugs caused by misunderstood default casts in C++ with generics and overloaded operators."  😃)

These kinds of awarenesses lead to a "defensive programming" mindset about casting, pointer math, increment as expression rather than operator, and most everything that can sometimes make the Go team seem arbitrary. What is not clear in this is that they miss these things too. Ken Thompson had the idea for the ternary operator. It makes sense to him. His (odd but logical) way of indenting if clauses and ternaries made it safe for him. But "good for Turing-award winner Ken" is not the test for professional team programming at scale. So when you hear these data-driven, career experience based arguments for/against approaches know that it is not personal, it is an analytic decision based on data. 

The goal of those decisions was to make whole teams more productive, and millions of Go users seem to bear these ideas out pretty well. You can see this play out in discussions of "what should be different in Go2?" Not much seems to be suggested, at least compared to each rev of C++.

MANAGING PAIN

Many new Go users complain about the verbosity of error handling. They feel pain. It is often stated as "it was easier and prettier when it was magic because of exceptions." The answer is always "embrace it. it is good for you to be explicit about possible errors. it gives you a place to do something smart rather than just fail. it is good for those who read your code to know what can go wrong. it is good for people who use your code to know what errors you can possibly have."

This may leave the new user doubtful, but nearly all who give it time come to see it as good advice. I'm not sure that the deeper point comes across as clearly as possible: the actual pain is probably not what you think it is. As a developer, you might think the effort/cost/pain is "how long to type it and get it to build and pass tests." But in truth, in teams, at scale, the real pain is 10000 people who read your API, 1000 people who read your code, 100 people who maintain/port/tune/extend it. The time it takes to type six lines instead of one is pretty small in this view, especially if it is true that each of these readers-of-code comes to understand it faster and more correctly. This is the real pain that needs to be managed. It makes tricky, dense, APL-ish cleverness seem awful and verbose but simple code rather nice.

Thoughts to consider,
Michael


--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.


--
Michael T. Jones
michae...@gmail.com

Nathan Youngman

unread,
Aug 15, 2018, 10:34:20 PM8/15/18
to golang-nuts
Mark,

Funny that -- you suggest that adding another syntax for the same behaviour "might help with adoption".

I think the opposite is true. 

My friend has been learning iOS development and has found many things confusing. There is the lambda syntax in Swift, and then the trailing lambda syntax (Scala does a similar thing, much like Ruby blocks). There is the already peculiar method signature that includes parameter names (a hold-over from Objective-C), but then there are exceptions to the rule as you go deeper. So many variations to keep straight and so many things to learn all at once.

What is the real benefit of this complexity?

No. I think Go's simplicity is what will help with adoption -- especially as brand new developers come online.

Software development isn't writing poetry. The goal isn't words that look nice on a page and cause you to ponder what the author was feeling. Maybe the verbosity of Go isn't as "enjoyable" to read as your favourite language, but it is easy to understand.

Nathan.

Sam Vilain

unread,
Aug 16, 2018, 2:13:57 AM8/16/18
to Axel Wagner, golang-nuts
Looking back at the PEP ( https://www.python.org/dev/peps/pep-0308/ ), it seems that the main argument was that people were working around the lack of it in ways that led to subtle bugs using 'and' and 'or'.  I seem to have inadvertently demonstrated that it's possible to do this in an obfuscated (and not 0-alloc) way in go, so in principle this same argument applies :-).  But unless my crazy ternary operator catches on (I assure you, I only *very* rarely use it in real code), this argument can be considered moot.

To me the biggest reason to want a ternary operator is to make it easier on the reader of the code.  If there's anything that go and python have in common, it's that both languages are designed to be easy to read (though with notable differences in emphasis about what makes it easy), and also relatively easy to tell whether code is correct.

With a ternary, it's very clear that after the statement, the assignment has occurred.

result = test ? "a" : "b"

With an `if' statement, you have to read a bit more:

var result string
if test {
  result = "a"
} else {
  result = "b"
}

In most cases the analysis is very simple to tell that both code branches assign to `result'.  But there's a problem with this.  This is only easy to see if someone doesn't then come along and insert extra code into those nice welcoming code blocks:

var result string
if test {
  result = "a"
  // insert code here
  funcCall()
} else {
  // insert other code here
  funcCall2()
  result = "b"
}

As a result, as the code is modified, the condition that 'result has a value' is not held and you end up with the zero value instead of something you expected.

Of course it's not as bad as in python or C because there is a separate `=' and `:=' operator, but as you can see above, this doesn't always come into play.  If you're assigning more than just an immediate constant, you probably don't want to start with `:=' and then re-assign based on the conditional.

It also gets much more complicated to follow for cases where you might want to use a nested or chained ternary expression, or a more complicated value construction which has many ternaries within it.

I've lost count of the times I've had to change:

return FooBar{
    Field: blah,
}

To:

var foo FooBar
if someCond {
    foo.Field = blah
} else {
    foo.Field = bar
}
return foo

In my experience code is clearer and easier to read if it does *not* contain re-assignments and branches, and temporary variables used only once.  With a ternary operator, you can code like that for cases where it makes sense.  I would much rather write:

return FooBar{
    Field: someCond ? blah : bar,
}

my 2¢,
Sam


To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.

Liam

unread,
Aug 16, 2018, 2:36:47 AM8/16/18
to golang-nuts
I long for a ternary which disallows embedded ternaries. I use this one-liner, but not happily:

v := a; if t { v = b }

This is not compatible with go fmt, but that tool's effects are undocumented (see issue 18790 which was declined), and it has no switches to disable/enable features. A syntax-aware sed is a good idea, but sadly go fmt destroys useful constructs. Other examples:

err := fn()
if err != nil { return err }

switch v {
case 1: pkg.one()
case 2: pkg.two()
case 3: pkg.thrice()

Kean Ho Chew

unread,
Aug 16, 2018, 3:48:46 AM8/16/18
to golang-nuts
The first is ternaries. What if only simple, non-nested ternaries were supported? For example, color := temperature > 100 ? “red” : “blue”. This seems so much more clear than the map[bool]:string trick that some have proposed. Writing this with an if statement takes either 4 or 6 lines. This comes up often enough in code to make a significant difference. I think it’s hard to argue that this has the potential to make code harder to read if they can’t be nested.

The thing about this is that no one ever considers the "learning curve" as a cost metric. The thing about ternary is that it encourages composite and hiding logics, as in this pattern:
color := <conditions> ? <positive action> : <negative action>

Your given example is straight and 1 of the use case. You probably don't see the cascading effect in other cases after enabling such feature. One is, as usual, new programmers tent to abuse it as such example (Notice the differences between "action" and "value"):
color := <condition1> ? (<condition2> ? <first action> : <second value>) : <last value>

Such composition only yields more complications. How to mitigate this? Most languages use more documentations, best practices, linters, scanners, machine review, more Medium blogging nonsense, yada yada etc. If you count those learning curves into the learning curve cost just to master the use language, you'll immediately realize why the Go developers team rejected it at the first place: keeping things simple and killing the root cause.

If you need a conditional logic, show it. Don't hide it. Show it CLEARLY and correctly, that is, if <condition>; else if <another condition>; else. It's easier to teach the new comers as young as 8 years old.

Lines of codes doesn't matters. Not even in C, the closest to the silicon next to Assembly. The compiler understands what to produce as an output binary; we, as developers, suppose to do our main and the very fundamental job: focus on writing your codes for human to read, even for yourself as well (when you revisit your codes after 2 years). LOC shouldn't even be used as a measurement for software quality assurance or performance from the start. It was meant for marketing hypes and a way to shoo the managers who don't read or maintain the codes from disturbing the developers at work. Arguing LOC is a cost is like arguing "we should not do paragraphing and numbering in English writing; Let's squeeze the entire T&C document in 1 straight line". 

This request is the same as against why Linux Kernel wants strictly 80 characters column but 8 characters indent discipline. A LOT of people thought it was meant for those developers who use terminal based editors like vim. In actual fact, it's not. (Article 1, Section 2, 3, 4 - https://www.kernel.org/doc/html/v4.10/process/coding-style.html).

Peace.

Wojciech S. Czarnecki

unread,
Aug 16, 2018, 4:06:29 AM8/16/18
to golan...@googlegroups.com
On Tue, 14 Aug 2018 05:43:07 -0500
Mark Volkmann <r.mark....@gmail.com> wrote:

> I’m new to Go and I imagine the idea of adding a ternary operator to Go has
> been discussed many times. Rather than repeat that, can someone point me to
> a discussion about why Go doesn’t add this?

> I’m struggling to understand why it is desirable to write code like this:
> if temperature > 100 {color = “red”} else {color = “blue”}

Because ternary expression (and some other often mentioned "go lacks")
unwittingly leads to code like this:

```
$templateParse = sub
{ s/([<>\|])([A-Za-z]+)(\d+)(\.+)/defined($2->[$3])?$1eq'|'?$2->[$3]:length($2->[$3])>length($&)?substr($2->[$3],0,length($&)):$1eq'>'?''x(length($&)-length($2->[$3])).$2->[$3]:$2->[$3].''x(length($&)-length($2->[$3])):defined($$2)?$1eq'|'?$$2:length(
$$2)>length($&)?substr($$2,0,length($&)):$1eq'>'?''x(length($&)-length($$2)).$$2:$$2.''x(length($&)-length($$2)):"BadData$1$2$3."/ego;}
```

Yes, this above is a SINGLE perl line instead of some 120+ lines of go's
"text/template" for similar functionality. Young cocky "pro" wrote this just
because he could. His older self longs much for ternaries, pre/post
increments, do{}while and a few other things but is so happy that young
rookies can't pollute code he is responsible for in ways he himself once
did. ;)

FYC

> R. Mark Volkmann
> Object Computing, Inc.

--
Wojciech S. Czarnecki
<< ^oo^ >> OHIR-RIPE

Axel Wagner

unread,
Aug 16, 2018, 4:11:49 AM8/16/18
to s...@vilain.net, golang-nuts
Hi Sam. A couple small responses inline.

On Thu, Aug 16, 2018 at 8:13 AM Sam Vilain <s...@vilain.net> wrote:
To me the biggest reason to want a ternary operator is to make it easier on the reader of the code.  If there's anything that go and python have in common, it's that both languages are designed to be easy to read (though with notable differences in emphasis about what makes it easy), and also relatively easy to tell whether code is correct.

I am not particularly familiar with the design process of Python. But judging from the result, I find that very hard to believe, TBQH. I find both of these things - reading Python and telling whether Python code is correct - to be very hard things to do. ISTM that if two processes claim to optimize for the same thing but achieve such different results, then either they weren't optimizing for the same thing, or one of the two failed. I'm uncomfortable claiming the latter, so I'd personally argue the former: That it may be worth considering that we should use different words for what Python aims for and what Go aims for (not that I have good suggestions).

With a ternary, it's very clear that after the statement, the assignment has occurred.

result = test ? "a" : "b"

With an `if' statement, you have to read a bit more:

var result string
if test {
  result = "a"
} else {
  result = "b"
}

In most cases the analysis is very simple to tell that both code branches assign to `result'.  But there's a problem with this.  This is only easy to see if someone doesn't then come along and insert extra code into those nice welcoming code blocks:

var result string
if test {
  result = "a"
  // insert code here
  funcCall()
} else {
  // insert other code here
  funcCall2()
  result = "b"
}

As a result, as the code is modified, the condition that 'result has a value' is not held and you end up with the zero value instead of something you expected.

I don't understand what you mean. Your code seems fine, what am I overlooking?

On a larger point: I don't believe this to be a good argument. I'd argue that if the logic of the latter is what you want, you will get there, ternary expression or not. I'd argue that in practice, if you started with a ternary expression, people would just remove that and write the code you ended up with anyway. Because they want that logic and writing that conditional statement is the way to achieve that logic.

But I'm not clairvoyant, so who knows what would happen in actual practice.
 
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts...@googlegroups.com.

roger peppe

unread,
Aug 16, 2018, 4:54:16 AM8/16/18
to Axel Wagner, s...@vilain.net, golang-nuts
In my experience, this is rarely a real pain, and where it is, a
simple function can help out.

func either(condition bool, a, b string) string {
if condition {
return a
}
return b
}

fmt.Printf("color is %s", either(temperature>100, "red", "blue"))

The runtime cost for simple cases is exactly the same as if it had
been built into the language. (If you wish to rely on the lazy
evaluation of the values with respect to the condition, I suspect that
an if statement would be more appropriate anyway.

On 16 August 2018 at 09:11, 'Axel Wagner' via golang-nuts

Axel Wagner

unread,
Aug 16, 2018, 4:59:00 AM8/16/18
to golang-nuts
func either(c bool, a, b func() string) string {
    if c {
        return a()
    }
    return b()
}

func thunk(s string) func() string {
    return func() string { return s }
}

fmt.Printf("color is %s", either(temperature > 100, thunk("red"), thunk("blue"))

</troll> ;)

Jesper Louis Andersen

unread,
Aug 16, 2018, 6:38:26 AM8/16/18
to r.mark....@gmail.com, golang-nuts
On Tue, Aug 14, 2018 at 6:43 PM Mark Volkmann <r.mark....@gmail.com> wrote:
I’m new to Go and I imagine the idea of adding a ternary operator to Go has been discussed many times. Rather than repeat that, can someone point me to a discussion about why Go doesn’t add this? I’m struggling to understand why it is desirable to write code like this:

var color
if temperature > 100 {
    color = “red”
} else {
    color = “blue”
}

Instead of this:

var color = temperature > 100 ? “red” : “blue”


I think the idea is that of a trade-off. Most people desire more expressive power, and it isn't a dismissal of the idea at all. However, it is important to note what you gain by trading off such an expression level construction, rather than just looking at what kind of demise it brings to programs. Leaving features out of a language can have a benefit as well because it allows you to explore different design paths.

The main benefit is that there are simpler semantics of expressions, since there is no selection operator in expressions. This avoids "clever programmers" who write incomprehensible code structure, hurting any newcomer to that part of the code base. You may argue that people shouldn't write bad code like this, but there is a definite basis in Go for keeping the language simple: it doesn't provide a whole lot of abstraction features in general.

The secondary benefit is that it limits boolean switches to just the statement syntactical group. This is somewhat aligned with the notion that the operators ++ and -- are at the statement level as well. Thus, to make control-flow decisions, one must use statements, and never expressions. This alters program design because it is entirely different code which flows "naturally" when writing.

So long story short: it isn't at all desirable to write code like that. But on the other hand, it isn't desirable to have a language which is harder to read, or harder to teach either. Adding ternary operators could be considered---after all the cognitive load of them isn't that high---but it was ultimately decided against.

A good approach here would be to look at languages which have that operator, index all the code in them and start looking at metrics:

* How often is the operator used?
* Given N experienced programmers, can they easily read the semantics for typical examples?
* Given N inexperienced programmers, does the same hold?
* Are there any situations in common use where the ternary operator adds expressive power so the alternative is ugly or hard to follow?

Running proper double-blind experiments on reading comprehension for programs is not something we've done a lot of. Sadly.

If you desire expression level operators to the point where you cannot live without them, you might enjoy functional languages. One of their defining features are that everything is an expression, and they don't distinguish between the statement and expression modality in syntax (which also removes them from the Algol base of languages). Hence, there are only "ternary" operators, by design.

Peter Waller

unread,
Aug 16, 2018, 7:34:46 AM8/16/18
to peterGo, golang-nuts
On Wed, 15 Aug 2018 at 12:44, peterGo <go.pe...@gmail.com> wrote:

That note from Stroustroup is profound. Thanks for sharing. Personally, I'm glad that we have a language where the philosophy is "What is the minimum feature-set which allows you to do anything you need to do", as opposed to the opposite approach which seems to be taken by many elements in the programming community. There is so much to be said for a system which can be learned in its entirely by anyone, by remaining small enough. Even with the small feature-set there is plenty of complexity to be getting on with ! ;-)

Giulio Iotti

unread,
Aug 16, 2018, 7:40:56 AM8/16/18
to golang-nuts
On Thursday, August 16, 2018 at 11:59:00 AM UTC+3, Axel Wagner wrote:
func either(c bool, a, b func() string) string {
    if c {
        return a()
    }
    return b()
}

func thunk(s string) func() string {
    return func() string { return s }
}

fmt.Printf("color is %s", either(temperature > 100, thunk("red"), thunk("blue"))

</troll> ;)

I know you are trolling and don't actually write such code, but for those who don't notice: if thunk() had side-effects, you would be in trouble.

-- 
Giulio 

ffm...@web.de

unread,
Aug 16, 2018, 12:01:20 PM8/16/18
to golang-nuts

var color
if temperature > 100 {
    color = “red”
} else {
    color = “blue”
}

IMHO, it could be argued about whether adding something like this would be useful:

color := if temperature > 100 {
    return “red”
} else {
    return “blue”
}

Haddock

unread,
Aug 16, 2018, 12:01:20 PM8/16/18
to golang-nuts
If you think this discussion is taking a lot of time have a look at the same discussion for Kotlin in their user group:

haskell...@yahoo.de

unread,
Aug 16, 2018, 4:53:14 PM8/16/18
to golang-nuts
color := colorFor(temperature)

func colorFor(temperature int) string {
if temperature > 100 {
return "red"
}
return "blue"

Matthias B.

unread,
Aug 16, 2018, 6:30:41 PM8/16/18
to golan...@googlegroups.com
I get the impression that the real issue here is that gofmt will break

if temperature > 100 { color = "red" } else { color = "blue" }

over multiple lines and that what the people asking for a ternary
operator really want is a one-liner. So ask yourselves, if gofmt were
to format your ternary operator (or the above suggested if-expression)
identical to the if statement, i.e. across the same number of lines,
would you still want it?

var color =
if temperature > 100 {
"red"
} else {
"blue"
}


var color =
temperature > 100 ?
"red"
:
"blue"


If you would NOT use these, your real issue is with gofmt, not the Go
language.


MSB

--
To understand recursion you must first understand recursion.

Liam Breck

unread,
Aug 16, 2018, 7:55:18 PM8/16/18
to Matthias B., golang-nuts
Indeed, the problem is largely go fmt, I already raised this, but no one picked up on it:

I use this one-liner:

v := a; if t { v = b }

This is not compatible with go fmt, but that tool's effects are undocumented (see issue 18790 which was declined), and it has no switches to disable/enable features. A syntax-aware sed is a good idea, but sadly go fmt destroys useful constructs. Other examples:

err := fn()
if err != nil { return err }

switch v {
case 1: pkg.one()
case 2: pkg.two()
case 3: pkg.thrice()
}

Christopher Nielsen

unread,
Aug 16, 2018, 8:19:12 PM8/16/18
to Liam Breck, Matthias B., golang-nuts
How is a ternary more readable? I've found that they make code more complex and unreadable, in both C and the proposed go, and the one-liner you provide is equally or more unreadable than the more verbose if statement.

Liam Breck

unread,
Aug 16, 2018, 8:53:15 PM8/16/18
to m4dh...@gmail.com, Matthias B., golang-nuts
I find that one-concept-per-line, yielding more compact functions, is easier to focus on, as it reduces vertical eye scanning and scrolling.

A single-line if stmnt and my switch example demonstrate one-concept-per-line.

go fmt aspires to produce an odd sort of poetry, which I find inefficient. It's especially bad in GitHub commits.

Providing a small menu of recommended project-wide formats, and allowing maintainers to tailor one for their needs would be most helpful. As is, I eschew it.

Hoo Luu

unread,
Aug 16, 2018, 10:14:54 PM8/16/18
to golang-nuts
Yep, the problem is due largely to gofmt. Ternary operator is easily misused and hard to read if it is nested. That's why i prefer if expression(without needing to add a new operator and even if it was deep nested in the worst case, its complexity would equal a deep nested if statement,its readability would no worse than if statement).

Kean Ho Chew

unread,
Aug 17, 2018, 12:07:41 AM8/17/18
to golang-nuts
On Friday, August 17, 2018 at 8:53:15 AM UTC+8, Liam wrote:
I find that one-concept-per-line, yielding more compact functions, is easier to focus on, as it reduces vertical eye scanning and scrolling.

A single-line if stmnt and my switch example demonstrate one-concept-per-line.

The thing is, what is cost with the current if else expression that worth more to introduce another possible complications? Like my previous email and other developers had implied, LOC doesn't matter. In the end, the compiler knows what to do.

It's okay to do vertical eye scanning (we're doing it anyway while we're coding/reviewing). When ternary logic is abused, we need to do vertical + horizontal scrolling. Let's fuzzy-logic the proposal:

Scenario:
1. We use the temperature and color to build a display.
2. We roll in updates (yellow & orange) in 2 years time.
3, We measure the number of possible steps with +1 and sum them up to weight its cost. Any sub-possible actions (e.g. number of replies in a forums) are ignored.

CASE I - Ternary
-----------
1. On first build: (+1)
color := temperature > 100 ? "red" : "blue"

2. Implement Upgrades with yellow & orange, assigned to fresh developer:
2.1 search for the code (+1)
2.2 no idea what is ternary operator, google and learn (+1)
2.3 realize how it works, upgrade it with: (+1)
color := temperature > 100 ? "red" : (temperature  > 75 ? "yellow" :  (temperature > 50 ? "orange" : "blue") )
2.4 submit to upstream. Got rejected by reviewer and request to change into using if else and/or switch cases. (+1)
2.5 if the developer went frustrated because 2.4 wasted his/her hours of research: raise a forum and go SPARTA (+1)
2.6 consensus achieved, implement: (+1)
color := "blue"
if temperature > 100 {
color = "red"
} else if temperature > 75 {
color = "yellow"
} else if temperature > 50 {
color = "orange"
}
2.7) Submit for review. (+1)
2.8) Reviewed and accepted (+1)

Total weight: +9
Total weight if the developer is not short-fused: +8 

----------
CASE II - Using if else statement
1. On first build: (+1)
color := "blue"
if temperature > 100 {
color = "red"
}

2. Implement upgrades with yellow & orange, assigned to fresh developer:
2.1 search for the code (+1)
2.2 Understood it is a nested if else statement. Add yellow and orange conditions: (+1)
color := "blue"
if temperature > 100 {
color = "red"
} else if temperature > 75 {
color = "yellow"
} else if temperature > 50 {
color = "orange"
}
2.3 Submit to upstream for review. (+1)
2.4 Accepted. (+1)

Total weight: +5

----------------------------------
Compare both processes:
CASE I (Ternary) - cost [8-9) points, possible forum discussion
CASE II (If Else) - cost [5] points

If we use time as the currency and learning curve as a metric, Case II is cheaper than Case I since it is a fixed cost (notice the bracket). This also means it's easier to transfer project from one person to another (because it not hard to learn).

Also, new comer is not necessary referring to new developer. It can also means season developer taking over a project he/she not familiar with. It can also means maintainer trying to learn a new patch as well.

My point is, wouldn't it be expensive to implement ternary just for the sake of saving a few lines of codes, which doesn't carry out any significant computational performances?


-- BETWEEN --

temperature > 100 ? "red" : "blue"

-- VS ---

color := "blue"
if temperature > 100 {
color = "red"
}

-------
I find the latter is easier to understand because it uses standard coding comprehension and without needing to research with google; which is a hell for new comer since trying to search "?" towards "ternary operations" without bugging a senior for the first time is scary googling experience.

p/s: I'm sorry if I'm harsh but I must be straight about protecting the simplicity of this language that I greatly fell in love with after dealing magics and horrors across Ruby, Python, C, C++, POSIX, etc. Go is beautifully crafted from learning to development; to process, and to release and distribution management standpoints. It's simple to understand, to learn, and to use.


go fmt aspires to produce an odd sort of poetry, which I find inefficient. It's especially bad in GitHub commits.

Go fmt standardizes code and facilitate self-learning, so that the new comer doesn't need to wonder around asking basic questions about formatting, how to write this and that etc. This encourages them to talks more about "problem", "algorithm" or "solution" to solve a problem, not mingling with the language itself in the conversation with the seasoned veterans. It is odd because it is the first language that provides such tool to scrutinize codes for free, out of the box.

bas...@gmail.com

unread,
Aug 17, 2018, 1:44:12 AM8/17/18
to golang-nuts
I disagree with the argument that a simple ternary could become unwieldy as a reason not to introduce it to a language.

People should be trusted (just as they are now with other constructs) to use a more suitable construct or to refactor when a given one fails to scale.  The fact that the Guilded Rose kata can be written with all it's nasty if/else nesting in Go, means regardless of whether a ternary expression is introduced or not, people will still write bad code.

RE the suggestion that a ternary wouldn't scale when new temperatures are required, use a switch.

RE the suggestion that Go already has ternary operators (with an example using `map`), this is far less readable than the ternary operator in most other languages and way less efficient.

RE the suggestion that a default case followed by an if statement can be used instead, I think that in the case of the ternary, the values are so intrinsically linked, that having a variable set across multiple lines is less readable.

Matthias B.

unread,
Aug 17, 2018, 4:17:03 AM8/17/18
to golan...@googlegroups.com
On Thu, 16 Aug 2018 16:54:35 -0700
Liam Breck <networ...@gmail.com> wrote:

> Indeed, the problem is largely go fmt, I already raised this, but no
> one picked up on it:
>
> I use this one-liner:
>
> v := a; if t { v = b }
>
> This is not compatible with go fmt, but that tool's effects are
> undocumented (see issue 18790
> <https://github.com/golang/go/issues/18790> which was declined), and
> it has no switches to disable/enable features.

Now THAT is something I can help you with:

https://github.com/mbenkmann/goformat

has the option "inlineblocks=keep" which should give you what you want.
And if it doesn't, you're welcome to contribute more switches.

MSB

--
Brains are the thing most fairly distributed on this world
because everyone thinks he's got more than anyone else.

Liam Breck

unread,
Aug 17, 2018, 4:52:36 AM8/17/18
to Matthias B., golang-nuts
Ah, very nice! Tho I can't tell from scanning the readme which features are different than go fmt.

Liam Breck

unread,
Aug 17, 2018, 5:56:05 AM8/17/18
to Kean Ho Chew, golang-nuts
People have a wide range of perceptive and cognitive abilities. People also have difficulty imagining the very different abilities that others possess.

I find constant vertical scanning and scrolling to be distracting and thus inefficient. So I won't use a tool that adds line breaks to my code, and I'd benefit from a one-line switch.

This is a good compromise, less cryptic than ?:

v = if t a; else b // else can be on next line
v = if (t) a; else b // parens isolate the test if required

And maybe that implies

v = switch t; case 1 a; case 2 b

Note that go fmt allows this clumsy construct 

if err = pkg.ActionItem(); err != nil { // which op is tested?
   return err
}

While exploding this clear one

err = pkg.ActionItem()
if err != nil { return err }

Kean Ho Chew

unread,
Aug 17, 2018, 8:54:32 AM8/17/18
to golang-nuts
On Friday, August 17, 2018 at 5:56:05 PM UTC+8, Liam wrote:
People have a wide range of perceptive and cognitive abilities. People also have difficulty imagining the very different abilities that others possess.
 
I find constant vertical scanning and scrolling to be distracting and thus inefficient. So I won't use a tool that adds line breaks to my code, and I'd benefit from a one-line switch.

My apology for shoving everyone into drinking tea hardly, forgotten to realize there are coffee lovers in the room too. 
 

This is a good compromise, less cryptic than ?:

v = if t a; else b // else can be on next line
v = if (t) a; else b // parens isolate the test if required

I'm willing to compromise only as far as this pattern, standing on tea sides for coffee folks:
v = b ; if (t) { v = a }
v
= b ; if (t) { a }
v
= b ; if t { a }
v
= b ; if t { v = a }


Here are the tests, based on comparing the readability, between the pattern vs. the Ternary:
// Oneline
v
:= "blue"; if data == nil { v = "red" }

v
:= data == nil   ? "red" : "blue"
v
:= (data == nil) ? "red" : "blue"


// multiple lines
status    
:= "blue";             if data == nil { status = "red" }
latitude  
:= device.gps("lat");  if data == nil { latitude := "000.00000" }
longitude
:= device.gps("long"); if data == nil { latitude := "000.00000" }

status    
:= data == nil ? "blue" : "red"
latitude  
:= data == nil ? device.gps("lat") : "000.00000"
longitude
:= data == nil ? device.gps("long") : "000.00000"


// nested conditions oneline
v
:= "blue"; if (data == nil && (device == nil || wifi == nil)) { v = "red" }

v
:= data == (data == nil && (device == nil || wifi == nil)) ? "red" : "blue"



// nested conditions multiple lines
status    
:= "blue";             if (data == nil && (bluetooth == nil || wifi == nil)) { status = "red" }
latitude  
:= device.gps("lat");  if (data == nil || accuracy < 30) { latitude := "000.00000" }
longitude
:= device.gps("long"); if (data == nil || accuracy < 30) { latitude := "000.00000" }

status    
:= (data == nil && (bluetooth == nil || wifi == nil) ? "blue" : "red"
latitude  
:= (data == nil || device.gps.accuracy() < 30)       ? device.gps("lat") : "000.00000"
longitude
:= (data == nil || device.gps.accuracy() < 30)       ? device.gps("long") : "000.00000"

Rationale:
1. Maintain clarity between control path and data path.

As I said earlier, I still see it is a costly feature vs vertical only source codes. I just can't see the value pursuing such direction. There must be a strong solid reason the developers who developed go did not implement such feature at the first place. Will I use it? No. If the core developers agreed to pursue, I can't object either but I do my part raising my past experience with that scary magical "?".


Note that go fmt allows this clumsy construct 

if err = pkg.ActionItem(); err != nil { // which op is tested?
   return err
}

While exploding this clear one

err = pkg.ActionItem()
if err != nil { return err }


because this is clearer?
err := pkg.ActionItem()
if err != nil {
     
return err
}

As far as I understand, to simplify, the above, line can be re-written to:
if err = pkg.ActionItem(); err != nil {

   
return err
}
Which is a clear 2 lines statement, error checking only. Anyway, I use the former. Again, I'm a tea folk. It's not a rule.



Tim Peoples

unread,
Aug 18, 2018, 4:16:44 PM8/18/18
to golang-nuts
Regarding your issues with the following...
 
I've lost count of the times I've had to change:

return FooBar{
    Field: blah,
}

To:

var foo FooBar
if someCond {
    foo.Field = blah
} else {
    foo.Field = bar
}
return foo

 ...I hardly consider that to be idiomatic Go.  A more go-like approach would be to follow the guideline to "return early" and write it more like this:

 if someCond {
  return FooBar{Field: blah}
}

return FooBar{Field: bar}

...which (IMHO) is markedly more readable than a ternary expression.

Liam Breck

unread,
Aug 18, 2018, 4:25:57 PM8/18/18
to Tim Peoples, golang-nuts
Normally the FooBar is full of other fields, necessitating the single field patch.

--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/MrpkS4epn_E/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages