"This little feature makes the code more compact and easy to read"
turns out to be wrong. There's no performance benefit, but it suggests
one. As a consequence, people tend to use this construct heavily to
increase performance, making code just plain ugly and distorted, without
any benefit. Write wrapper functions instead.
Surma
?! Really?
I've never ever thought of if-expressions of having a performance
benefit; it's purely an expressiveness benefit (compactness,
readability, and doing what it says on the can and no more).
Things like
return (a ? b : c)
f( x, (y ? z: w), h )
a = (b ? c : d)
are all, to me, more readable than the tangled dependencies
that you need to express the same effect without them.
(The C micro-syntax for it I unprefer, but that's a minor detail.)
Chris
--
Chris "allusive" Dollin
That only says that it doesn't, and gives the solution in
the trivial case. (For example, very often you have to
introduce the variable n because it doesn't already
exist, and you have to type it, because the initialisation
statement isn't to hand).
Yes, of course one can manage without it. One can manage
without :=, return variables, and statement preambles for if
conditions as well.
I also see no compelling downside/risk for this "syntax sugar," and
make quite effective use of it, when it's available.
Compare
func will_it_blend(unfortunate_object interface{}) bool {
// processing
return pieces > 100 ? true : false // straightforward
}
with
func will_it_blend(thing interface{}) bool {
// processing
if pieces > 100 {
return true
}
return false // unnecessarily verbose and arguably ugly
}
You picked an unfortunate example.
func will_it_blend(unfortunate_object interface{}) bool {
// processing
return pieces > 100 // even more straightforward
}
For that I find myself doing something like:
transliterate := map[bool]string{true: "are not", false: "are"}
s := fmt.Sprintf("You %s a featherless biped", transliterate[isChicken])
(though generally I'd have the map defined outside the func so it's
only created once at import time)
This looks very nice and readable to me, without even considering the
obvious spelling:
func will_it_blend(thing interface{}) bool {
return pieces > 100
}
I'm sure you can come up with a more compelling example than you have,
but here is a compelling example *not* to have it, though:
rc = rc < 0 ? rc : add_void_completion(zh, h.xid, 0, 0);
rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
get_buffer_len(oa));
return rc<0 ? rc : adaptor_send_queue(zh, 0);
and another one:
fallback('cancelControl', (options.cancelLink ? 'link' :
(options.cancelButton ? 'button' :
options.cancelLink == options.cancelButton == false ? false : undefined)));
fallback('okControl', (options.okLink ? 'link' : (options.okButton ?
'button' :
options.okLink == options.okButton == false ? false : undefined)));
These are taken from real code, and unlike the alternatives, they're
not readable anymore.
I don't miss much using the ternary operator, and definitely don't
miss *at all* reading it back.
--
Gustavo Niemeyer
http://niemeyer.net
http://niemeyer.net/blog
http://niemeyer.net/twitter
This argument applies equally to every language feature.
I'm receptive to the idea that a complex feature should be avoided to
keep the grammar/parser/toolchain/etc. straightforward (eg. generics).
I'm receptive to the idea that a feature should be avoided if it is
(provably or self-evidently) easily/often abused (eg. operator
overloading or default arguments).
I'm even receptive to the idea that a given piece of syntax sugar
should be avoided because it is somehow outside of the "philosophy" of
a language (eg. feature requests that would bend Go's strict
formatting rules).
But I'm not receptive, and actually quite perplexed, by opposition to
a feature that imposes no cost on the compiler toolchain and is by any
measure highly idiomatic, simply on the strength of a hypothetical and
irregular _potential_ for misuse. Maybe I'm missing something.
That's where I personally classify the ternary operator. Little
benefit, often abused.
Just an opinion, though.
Let's rewrite this using if-statements:
if rc < 0 { rc = rc; } else { rc = add_void_completion(zh, h.xid, 0, 0); }
What? That's silly? Well, if one can object that that is silly,
one can object that the "compelling example" is silly too.
People will write silly code -- I know I have -- using any
feature of a programming language -- nested arithmetic
expressions, function calls, initialised declatations, break
(and (fx:spit) continue), first-class functions, inheritance,
&co. Even an advocate for conditional expressions (I would
put myself in that class) doesn't require that every condition
be an expression.
Since that code is /hiding/ that rc need only be updated if
if its >= 0, it's not good code /for a conditional expression/
and is compelling example of "don't do that".
> rc = rc < 0 ? rc : queue_buffer_bytes(&zh->to_send, get_buffer(oa),
> get_buffer_len(oa));
Likewise.
> return rc<0 ? rc : adaptor_send_queue(zh, 0);
This, however, seems appropriate. You're going to return
something; what you return depends on rc.
> and another one:
>
> fallback('cancelControl', (options.cancelLink ? 'link' :
> (options.cancelButton ? 'button' :
> options.cancelLink == options.cancelButton == false ? false : undefined)));
> fallback('okControl', (options.okLink ? 'link' : (options.okButton ?
> 'button' :
> options.okLink == options.okButton == false ? false : undefined)));
>
> These are taken from real code, and unlike the alternatives, they're
> not readable anymore.
You can't enforce readability in the syntax, and you don't
need to: that's what social processes -- like code reviews and
pair programming -- are for.
> I don't miss much using the ternary operator, and definitely don't
> miss *at all* reading it back.
I miss it every day, but that in itself isn't a good argument for it.
I agree that the conditional expression is a good thing to have; it
makes coding easier and more expressive. Witness the fact that Python
introduced it, albeit with the wrong choice of syntax. I think Go would
be better if it had a conditional expression, so much would be so much
easier.
The argument that "it is dangerous", is I think totally fallacious, and
indeed FUD. Similar in style to the argument that the for statement is
redundant because there is a while statement.
[ . . . ]
--
Russel.
=============================================================================
Dr Russel Winder t: +44 20 7585 2200 voip: sip:russel...@ekiga.net
41 Buckmaster Road m: +44 7770 465 077 xmpp: rus...@russel.org.uk
London SW11 1EN, UK w: www.russel.org.uk skype: russel_winder
Ok, I can't help you specifically, then. ;-)
> You can't enforce readability in the syntax, and you don't
Indeed, but I'm generally very happy to help where I can.
Python is a totally different language to Go. Python already had
myriad ways of writing unreadable code, so I can understand they had
no qualms introducing another.
> I think Go would
> be better if it had a conditional expression, so much would be so much
> easier.
We're genuinely open to suggestions. If anyone can propose syntactical
improvements that are worth their weight, we'd be keen to adopt them.
(and we have, in the past)
So far this thread has painted a pretty clear picture of the benefits
and drawbacks the ternary operator. Gustavo's examples are
particularly illustrative.
Andrew
I don't much like the C ternary operator, but I do miss being able to
use conditional expressions. I really like in Haskell being able to
write the equivalent of:
count := switch y := f(x); y {
case 0: "no widgets"
case 1: "one widget"
default: fmt.Sprint(y, " widgets")
}
Unfortunately, it's hard to see how this would work with go, which is
imperative rather than declarative, and conditionals always involve
statements rather than expressions.
David Roundy
I suspect that this would break the grammar, or make it rather
fragile. (Could be wrong.) My deeper reservation is that makes
if-expressions lexically heavy, which is not what I'd want. Compare
the C-style:
max := a > b ? a : b
or Algol68-style:
max := (a > b | a | b)
or adhoc:
max := (? a > b : a : b ?)
> Sure, I like the C-style "?" operator. But I would also like to use
> "switch" in an expression context. How would you spell that?
I would personally spell that as E-r-l-a-n-g or H-a-s-k-e-l-l. :-)
C-style in Go?
wossname :=
x == A ? yes1 :
x == B ? yes2 :
x == C ? yes3 :
otherwise
looks readable enough to me. (Personal preference would put the
:s at the beginning of the test line, not the end, but Go's semicolon-insertion
rule makes that infeasible. Unless, I suppose, we allow semicolons
before : in the syntax.)
For a switch I'm happier for the lexical load to be heaver because
I expect there to be more cases anyway: if push came to shove I'd
retreat into history and suggest we use BCPL's valof-resultis syntax.
Agreed, and I'm sure you'll also agree with this version:
The fact that other good languages have a feature is not a good
argument for adding the same feature to Go.
> I'm just saying that I like to put (simple) conditionals within
> expression and this is something I miss in Go. I still think Go is
> very nice language even without this particular feature.
The point I was implying with my joke is that you are requesting a
feature common to functional languages which doesn't fit well with the
overall structure of Go.
> The point I was implying with my joke is that you are requesting a
> feature common to functional languages
And non-functional ones: C & C++, BCPL, Java, and Ruby, for example.
And Go is more function-friendly than all of those bar Ruby (by virtue
of having full lexical scope & function literals).
> which doesn't fit well with the overall structure of Go.
How so?
> And non-functional ones: C & C++, BCPL, Java, and Ruby, for example.
You seem to have missed the statement context. Switches are not
expressions in these languages.
Oops. But:
> Switches are not expressions in these languages.
They are in Ruby (but "switch" is spelt "case" and "case" is spelt "when"),
and in BCPL, you use VALOF-RESULTIS to get switch expressions out
of switch statements. Algol 68 also has a switch expression but it's the
horrible kind (the cases are labelled with ascending integers) and I
can't remember the long-form spelling.
So even with the correction to switch syntax there are multiple
non-functional languages with switch expressions.
Chris
Looks like Bliss had them too.
--
Chris "allusive" Dollin