60 ms·
> Python programmers in general have an irrational aversion to if-expressions, a.k.a. the “ternary” operator. Because the Python ternary operator is fucking ba
by ohazi 5y ago
> Python programmers in general have an irrational aversion to if-expressions, a.k.a. the “ternary” operator.
Because the Python ternary operator is fucking backwards! Why on earth would you put the condition in the middle!?
x = 4 if condition() else 5
vs.:
condition() ? 4 : 5
vs. the author's own lisp example:
(setq x (if (condition) 4 5))
I love ternary operators, and Lisp/ML-style if else blocks that return things, but Python puts the 4 and the 5 so far away from each other that even I hate using it, especially when condition is a chained mess rather than just a function call.
- chakkepolja 5y agoIn practice it's not that bad. One bad thing is you can't write long chains as you can do with C-style ternary operator.
- Cthulhu_ 5y agoThat's not a bad thing, long ternary chains are illegible. You should never have more than one condition in a ternary.
- pdpi 5y agoThere are some specific shapes of chained ?: that are fairly legible. Eg c = condA ? valA : condB ? valB : defaultVal;
- DonHopkins 5y agoFor Turing's sake, if you insist on chaining ternary operators, then you should always use parens instead of showboating that you cleverly-by-half memorized a particular language's obscure precedence and associativity rules. PHP in all its glory and splendor actually originally used left-to-right instead of right-to-left associativity for the ternary ?: operator, so that expression you wrote works differently in PHP and (JavaScript or C or any other language). (Side note: notice how I used parens instead of depending on you guessing about the relative precedence of "and" and "or" in English.) It's easy (and perfectly valid) to blame the designers of PHP for not knowing or caring or paying much attention to the syntax of the programming languages they were imitating, and doing something that stunningly stupid, but it just goes to prove that many people (especially PHP designers like Rasmus "I don't care about this crap at all" Lerdorf and typical PHP programmers) don't even have the faintest idea what the precedence and associativity of many of their favorite programming language's operators are, or what those terms even mean, and they just willy nilly cargo cult copy and paste and translate between PHP and JavaScript code examples from Stackoverflow, introducing terrible hard-to-spot bugs. https://en.wikiquote.org/wiki/Rasmus_Lerdorf https://en.wikiquote.org/wiki/Rasmus_Lerdorf >"We have things like protected properties. We have abstract methods. We have all this stuff that your computer science teacher told you you should be using. I don't care about this crap at all." -Rasmus Lerdorf But the real fault lies not just with PHP for parroting C incorrectly, but with C for doing something that stupid and hard to remember and easy to get wrong in the first place. Please always use parens for that kind of stuff, because even if YOU memorized and love to depend on and show off your knowledge of the precedence and associativity rules of the language you're using, the people reading and modifying and maintaining your code probably don't. https://wiki.php.net/rfc/ternary_associativity https://wiki.php.net/rfc/ternary_associativity >PHP RFC: Deprecate left-associative ternary operator >Unlike most (all?) other languages, the ternary operator in PHP is left-associative rather than right-associative. The left-associative behavior is generally not useful and confusing for programmers who switch between different languages. This RFC proposes to deprecate and remove left-associativity for the ternary operator and require explicit use of parentheses instead. >As an example, the code return $a == 1 ? 'one' : $a == 2 ? 'two' : $a == 3 ? 'three' : $a == 4 ? 'four' : 'other'; >would in most (all?) other languages be interpreted as return $a == 1 ? 'one' : ($a == 2 ? 'two' : ($a == 3 ? 'three' : ($a == 4 ? 'four' : 'other'))); >which is both the useful and intuitive interpretation. In PHP, it is instead interpreted as return ((($a == 1 ? 'one' : $a == 2) ? 'two' : $a == 3) ? 'three' : $a == 4) ? 'four' : 'other'; >which is generally not what was intended. https://stackoverflow.com/questions/20559150/ternary-operator-left-associativity https://stackoverflow.com/questions/20559150/ternary-operato... >Q: Can someone please explain what is happening here and why it is printing 'four'? >A: Because your whole expression evaluates as if it was (......) ? 'four' : 'other'. Since the first element is probably something truthy, it gives you 'four'. In saner languages, where ?: has right associativity, the whole expression evaluates as if it was $a == 1 ? 'one' : (......), where if $a is not 1, you go on to test other things. http://phpsadness.com/sad/30 http://phpsadness.com/sad/30 >PHP Sadness: Ternary operator associativity >The ternary operator is left-associative and therefore behaves entirely incorrectly: https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/ https://eev.ee/blog/2012/04/09/php-a-fractal-of-bad-design/ >PHP: a fractal of bad design >Unlike (literally!) every other language with a similar operator, ?: is left associative.
- water8 5y agoStill beats APL
- DonHopkins 5y agoNo, consistency within a language is good! https://en.wikipedia.org/wiki/APL_(programming_language)#Design https://en.wikipedia.org/wiki/APL_(programming_language)#Des... >All primitives are defined to have the same precedence, and always associate to the right. Thus, APL is read or best understood from right-to-left. But consistency across all languages is too much to ask for, except when one language is purposefully imitating another language's syntax but subtly changing it, like PHP did to C. Or the way Perl and PHP imitated C's syntax for references with &, implying that they were somehow alike and could be used in similar ways, even though they're totally different foot guns. At least APL and FORTH and LISP pick a lane (albeit different sides of the road), and stay in it.
- chrismorgan 5y agoAs PHP’s RFC says, right-associative ternary is useful and intuitive. I don’t need no parentheses, the meaning is clear and obvious and doesn’t go wrong in any environment that I or those I work with use, and doesn’t even go wrong in PHP any more (warning in 7.4, syntax error in 8). I’m not going to compromise the beauty and editability of my code by inserting superfluous parentheses—they’d make me have to think more about the meaning of the ternary chain, not less.
- karatinversion 5y agoI don't think it's backwards, it's just different than C and derivatives, and makes different things simple. I quite like the x = ( 1/y if y > 0 else -1/y if y < 0 else nan()) idiom.
- NwtnsMthd 5y agoThank you! For years I've tried to figure out how to chain the Python ternary operator while maintaining readability, and here it is!
- fantod 5y ago> Why on earth would you put the condition in the middle!? For readability.
- MathMonkeyMan 5y agoBecause that's what Guido liked, that's the answer. I don't like it much, but it's neat that the "happy" path value is a prefix of the assignment statement, i.e. in x = 4 if thing() else -1 there is a prefix x = 4 which, if the predicate is "expected," is like saying x = value (... unless blah blah) which, I could see some people liking (maybe Dutch people). Let's just write everything in Scheme for God's sake JOIN US.
- jstanley 5y ago> which, if the predicate is "expected," is like saying > x = value (... unless blah blah) Except it's the opposite of that, because "if" is the opposite of "unless". It's more like saying x = value (... if blah blah)
- MathMonkeyMan 5y agoGood point. The "blah blah" in my case would have to negate the condition. x = value (... unless this isn't true, then it's blah instead) The stuff in parentheses is a caveat. Works just as well your way, and more directly. x = value (... so long as this is true, otherwise blah)
- wodenokoto 5y agoI’ll give a shot at answering your rant with another one: I’ve never figured out those question marks. And in your example, what the hell even happens if condition is true or false? At least the lisp version has a function call that includes the word set, so you can kinda guess that the x variable is set. The if-expression reads very close to English, so you don’t need to look it up in the manual to read it. I wouldn’t even know what to query to look up `code ? Code : code` in a manual / google. `setq` is also easy to look up. The question mark colon loses on all counts in my book. I hate it.
- rjsw 5y agoThe C example doesn't match the others as the assignment is missing. It should be: x = condition() ? 4 : 5
- drcongo 5y agoI love the Python version, it reads like English. Use umbrella if raining, else wear shades.
- DonHopkins 5y agoDo you mean "use(raining ? umbrella : wear(shades))" or "raining ? use(umbrella) : wear(shades)"? Personally I prefer FORTH, which like Yoda reads, and unambiguously puts the condition first, the IF second, the ELSE third, and the THEN last. FORTH ?KNOW IF HONK! ELSE FORTH LEARN! THEN FORTH doesn't have expressions, parens, precedence, or associativity rules, it just has a stack (or rather, two stacks: operand and return; or three of you count the vocabulary search stack). ( And actually it does have parens, but they are for comments! )
- Zababa 5y agoIs this how you would say it in English though? I think "If raining use umbrella else wear shades" is closer to regular English.
- deleted 5y ago[deleted]
- Hizonner 5y agoIf you don't like awkward ugly clunky syntax, you probably shouldn't program in Python. And, for that matter, if you like correctness checking, you also shouldn't program in Python...
- justusthane 5y agoSerious question, what alternative(s) to Python would you recommend with nicer syntax?
- wffurr 5y agoScheme or Go.
- Hizonner 5y agoWell, to be fair, sometimes, especially for short scripts, you're stuck with Python because you know it's going to be available. Personally, from a syntax point of view, as opposed to an easy availability point of view, I prefer Haskell, or Lisp if you can live with the parentheses. Haskell has some syntactic oddities, and the support for infix operators has encouraged the ecosystem to define way too many obscure </?$+/>-looking things, but the syntax mostly just gets out of your way and is relatively regular. I do admit that if you're going to be infixy anyway, a C-like "?:" ternary is nice. C got its expressions right. It is, of course, objectively true that one of the things Python does get right about syntax is the significant whitespace, and Haskell has that too (without the stupid colons). Curly braces are the work of the Devil, and "BEGIN" and "END" are just unmentionable.
- marcosdumay 5y agoI don't think Python gets semantic whitespace right. It requires extraneous parenthesis and has an impact on lambda functions, making them way less powerful than they should be. Haskell got it right.
- rubyist5eva 5y agoRuby for me. I program in both, primarily Ruby with a few python microservices and Ruby’s syntax is similar to Python in a lot of cases but much easier and nicer to break out for something “nicer” to look at. All in my opinion of course.
- sneak 5y agoI prefer perl's style, which IIRC is also in ruby and coffeescript: x = 5 x = 4 if condition()
- rubyist5eva 5y agoRuby also has a C-like ternary operator. Many options for people that prefer them :)
- rcthompson 5y agoI think the Python ternary is best read as "DEFAULT_VALUE if COMMON_CASE else FALLBACK", and is best used in situations where this is what is meant semantically. If you're dealing with two equally plausible code paths, the standard if/else branching structure makes that more clear.
- albrewer 5y agoThis is how I use it. If I want an empty list as a default argument for some function, I do something like this: def foo(things: Optional[List[str]] = None) -> None: thing_list = things if things else [] ...
- hatmatrix 5y agoI prefer this Python syntax: x = condition() and 4 or 5
- klyrs 5y agoIf you know the first value is truthy, sure. x = condition() and [] or True is not equivalent to x = [] if condition() else True Now, I have seen a genuine application of `X and A or B` before. That occurs when you're checking a condition before, say, popping from a list -- but if the list is empty, you still want a default value.
- hatmatrix 5y agoCan you give me an example where the two return different values?
- klyrs 5y agoWhen condition() is truthy, the first evaluates to True and the second evaluates to [] Try it out. https://ideone.com/GciAs8 https://ideone.com/GciAs8
- hatmatrix 5y agoSo the problem is not in condition() but in the the other statement/expression. Similarly, True and False or 5 returns 5 rather than False.
- Uberphallus 5y agoI love Python but my personal hate goes for this when used in list comprehensions. a = [1,2,3] # list comprehension with if [ x for x in a if x > 1] [2, 3] # list comprehension with if/else [ x if x > 1 else x*2 for x in a] [2, 2, 3] When it's just "if" it goes after the "for", when it's "if/else" it goes, all of it, before. I still don't understand why it's this way, it doesn't even make sense even reading it in natural language. I much prefer the mathematical-like syntax present in Scala for this. Edit: Thanks for the responses, they are are insightful, never thought of this that way. Still in my head the lack of "else" clause implies filtering no matter where, while its presence implies transformation no matter where. The first element of the list is always transformed (though in this example it's identity). I think it's a case of Python trying to do too much with too little. Personally for anything relatively complicated I still prefer to use filter() and map() as list comprehensions can get unwieldly quick.
- inanutshellus 5y agoThe "before" is what value is returned, the "after" is whether it should be returned. In your case you were conditionally choosing a manipulated return value for x and didn't care at all about filtering the list. Here's one with both: [x if x > y else x*2 for x in a if x % 2 == 0] ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ return x as... filter down to only... x or x*2 even 'x's There's not really a semantically logical place for an "else" in the latter half because it's only looking for a true statement so it can return x (or not). For example this makes sense: ... if x % 2 == 0 or x == 3 (filter the list to even values of x or the value 3) but this does not: ... if x % 2 == 0 else x == 3 (filter the list to even values, otherwise wait, otherwise?)
- pierrebai 5y agoThe point is not that we're idiots who don't know the language, but that functional-style ordering does not align with human cognitive cues. The problem is that functional-style fails to separate essential bits and fails to give strong visual clues where each bits begins and ends. That is why the old imperative style is clearer. Compare: for i in container: do something with i and [do something with i for i in container] One has a clear separation of loop and action, the other requires scanning for the "for i" in the middle. This generalize to other similar construct, and the Python ternary operator has the same flaw. The imperative if / else version clearly separate alternatives on separate lines. IMO, syntax design should always try to learn from the imperative style and always strive to produce a syntax that clearly separate each logical item in a compound statement, and using different lines is one of the best for us human to visually grok. (That's why even Lisper will often format their code to separate bits on different lines!)
- westurner 5y agoAs I recall, object? and object?? are and work in IPython because the Python mailing list said that the ternary operator was not reserved. (IIRC there was yet no formal grammar or collections.abc or maybe even datetime or json yet at the time). Ternary expressions on one line require branch coverage to be enabled in your e.g. pytest; otherwise it'll look like the whole line is covered by tests when each branch on said line hasn't actually been tested. .get() -> Union[None, T]