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

Suggestion: implement ternary conditionals #3239

Open
mklement0 opened this Issue Mar 2, 2017 · 19 comments

Comments

10 participants
@mklement0
Contributor

mklement0 commented Mar 2, 2017

C-style ternary conditionals would be a handy addition to the language.

For instance, instead of writing:

if ((get-date).tostring("ss") % 2) { 'odd'  } else  { 'even' }

one could write:

(get-date).tostring("ss") % 2   ?   'odd'   :    'even'

It would also relieve a long-standing disappointment:

At Microsoft, “to ship is to choose”. One of the things we were very disappointed in not being able to ship in V1.0 is a ternary operator.

From a PowerShell Team blog post dated 29 December 2006.


Related: implement null-coalescence and null-soaking

@BrucePay

This comment has been minimized.

Show comment
Hide comment
@BrucePay

BrucePay Mar 2, 2017

Member

Note that with the change to allow assignments from statements, the need for a ternary operator is reduced. You can simply do
$var = if ($x) { $x } else { $y }
It's not as concise as the ternary operator but is arguably more readable.

Member

BrucePay commented Mar 2, 2017

Note that with the change to allow assignments from statements, the need for a ternary operator is reduced. You can simply do
$var = if ($x) { $x } else { $y }
It's not as concise as the ternary operator but is arguably more readable.

@mklement0

This comment has been minimized.

Show comment
Hide comment
@mklement0

mklement0 Mar 2, 2017

Contributor

Yes, though you still have to use $(...) in addition if the conditional is part of a larger expression:

'The current sec. is ' + $(if ((get-date).tostring("ss") % 2) { 'odd'  } else  { 'even' })

vs.

'The current sec. is ' + ((get-date).tostring("ss") % 2   ?   'odd'   :    'even')

I know that readability is in the eye of the ... uh ... reader, but I personally find the latter visually easier to parse, and having to type less is always a bonus.

With such a frequently used feature, I think remembering the more abstract ternary syntax wouldn't be a problem (and, of course, people can continue to use if, if they prefer).

Contributor

mklement0 commented Mar 2, 2017

Yes, though you still have to use $(...) in addition if the conditional is part of a larger expression:

'The current sec. is ' + $(if ((get-date).tostring("ss") % 2) { 'odd'  } else  { 'even' })

vs.

'The current sec. is ' + ((get-date).tostring("ss") % 2   ?   'odd'   :    'even')

I know that readability is in the eye of the ... uh ... reader, but I personally find the latter visually easier to parse, and having to type less is always a bonus.

With such a frequently used feature, I think remembering the more abstract ternary syntax wouldn't be a problem (and, of course, people can continue to use if, if they prefer).

@thezim

This comment has been minimized.

Show comment
Hide comment
@thezim

thezim Mar 2, 2017

Contributor

@mklement0 I have to agree later has less cognitive load.

Contributor

thezim commented Mar 2, 2017

@mklement0 I have to agree later has less cognitive load.

@SteveL-MSFT SteveL-MSFT added this to the 6.1.0 milestone Mar 4, 2017

@powercode

This comment has been minimized.

Show comment
Hide comment
@powercode

powercode Feb 5, 2018

Collaborator

This is definitely on my top list to.
@BrucePay, are there any reasons that this would be a bad idea? Or is it just about 'to ship is to choose' ?

Collaborator

powercode commented Feb 5, 2018

This is definitely on my top list to.
@BrucePay, are there any reasons that this would be a bad idea? Or is it just about 'to ship is to choose' ?

@lzybkr

This comment has been minimized.

Show comment
Hide comment
@lzybkr

lzybkr Feb 5, 2018

Member

In the past, this feature request was turned down as being harder for less experienced scripters to understand, and that the expression form of the if statement was a clearer, though more verbose alternative.

My personal perspective: if the language was just for me, I'd probably have added it a long time ago. But ... I find many developers don't use it, which actually suggests there is some truth to the hypothesis that less experienced folks will have trouble with the ternary operator.

Member

lzybkr commented Feb 5, 2018

In the past, this feature request was turned down as being harder for less experienced scripters to understand, and that the expression form of the if statement was a clearer, though more verbose alternative.

My personal perspective: if the language was just for me, I'd probably have added it a long time ago. But ... I find many developers don't use it, which actually suggests there is some truth to the hypothesis that less experienced folks will have trouble with the ternary operator.

@mklement0

This comment has been minimized.

Show comment
Hide comment
@mklement0

mklement0 Feb 11, 2018

Contributor

@lzybkr:

It's worth distinguishing between active use of a feature vs. the ability to recognize and understand it.

Clearly, everyone gets to choose whether to use such a feature, but are you saying that "have trouble with" means that less experienced folks won't understand it when they see it in the code of others?

Contributor

mklement0 commented Feb 11, 2018

@lzybkr:

It's worth distinguishing between active use of a feature vs. the ability to recognize and understand it.

Clearly, everyone gets to choose whether to use such a feature, but are you saying that "have trouble with" means that less experienced folks won't understand it when they see it in the code of others?

@lzybkr

This comment has been minimized.

Show comment
Hide comment
@lzybkr

lzybkr Feb 12, 2018

Member

@mklement0 - we introduced $psitem as an alias for $_ because of sufficient feedback that $_ was cryptic and confusing, so I do believe the ternary operator would be difficult for some less experienced people to understand.

Member

lzybkr commented Feb 12, 2018

@mklement0 - we introduced $psitem as an alias for $_ because of sufficient feedback that $_ was cryptic and confusing, so I do believe the ternary operator would be difficult for some less experienced people to understand.

@powercode

This comment has been minimized.

Show comment
Hide comment
@powercode

powercode Feb 12, 2018

Collaborator

On the other hand, there are many elements in PowerShell that are confusing to beginners, or even to quite experienced developers.

As with any other language, it takes some effort to learn the syntax and meaning of language constructs.
I don't know if I think the ternary operator is especially difficult to grasp.

Do you have data suggesting that it is?

Collaborator

powercode commented Feb 12, 2018

On the other hand, there are many elements in PowerShell that are confusing to beginners, or even to quite experienced developers.

As with any other language, it takes some effort to learn the syntax and meaning of language constructs.
I don't know if I think the ternary operator is especially difficult to grasp.

Do you have data suggesting that it is?

@lzybkr

This comment has been minimized.

Show comment
Hide comment
@lzybkr

lzybkr Feb 12, 2018

Member

I believe the only data is anecdotal, but the criticism applies to any cryptic language - bash, perl, etc.

And to be clear, I was providing the historical context. Maybe PowerShell is ubiquitous enough now that more cryptic syntax won't affect adoption.

Also keep in mind - some terse languages still use if/then as the ternary operator, e.g. F#.

That said, maybe it's possible to use fewer characters and not be too cryptic:

if ($x) { $y } else { $z }
$x -then $y -else $z
$x ? $y : $z

-then/-else fits well with PowerShell syntax - but, an operator (or operators if it's too confusing to think of as a single operator), you gain the benefit of not needing parens and braces.

Then again, this is asking for trouble similar to foreach vs. foreach-object. but maybe it's not as bad, I don't know.

Member

lzybkr commented Feb 12, 2018

I believe the only data is anecdotal, but the criticism applies to any cryptic language - bash, perl, etc.

And to be clear, I was providing the historical context. Maybe PowerShell is ubiquitous enough now that more cryptic syntax won't affect adoption.

Also keep in mind - some terse languages still use if/then as the ternary operator, e.g. F#.

That said, maybe it's possible to use fewer characters and not be too cryptic:

if ($x) { $y } else { $z }
$x -then $y -else $z
$x ? $y : $z

-then/-else fits well with PowerShell syntax - but, an operator (or operators if it's too confusing to think of as a single operator), you gain the benefit of not needing parens and braces.

Then again, this is asking for trouble similar to foreach vs. foreach-object. but maybe it's not as bad, I don't know.

@mklement0

This comment has been minimized.

Show comment
Hide comment
@mklement0

mklement0 Feb 19, 2018

Contributor

@lzybkr:

Cryptic in homeopathic doses is healthy:

PowerShell is commendably not cryptic overall, but in some cases offers cryptic syntax as a concise alternative (you still can be verbose if desired) for frequently used constructs, notably ? for Where-Object and % for ForEach-Object.

Providing $x ? $y : $z as a concise alternative to if ($x) then { $y } else { $z } to me is in the same spirit.

The cryptic aspect is ameliorated by ? being reminiscent of a question and therefore suggesting a conditional (albeit one preceding the ? in this case), and - more importantly - being a potentially familiar construct from several other languages, with the same fundamental semantics.

You don't get the same benefit with $x -then $y -else $z: it is not familiar, and that a conditional should go before -then is not obvious.

Also, while many PS operators do have symbolic names, many do not: * / + % ...
These are inherently cryptic too, we just don't perceive them that way anymore, because they are so familiar and ubiquitous.

My sense is that $x ? $y : $z too is already familiar to many, and will become even more so once introduced into the language, given the frequent need for concise conditionals.

Contributor

mklement0 commented Feb 19, 2018

@lzybkr:

Cryptic in homeopathic doses is healthy:

PowerShell is commendably not cryptic overall, but in some cases offers cryptic syntax as a concise alternative (you still can be verbose if desired) for frequently used constructs, notably ? for Where-Object and % for ForEach-Object.

Providing $x ? $y : $z as a concise alternative to if ($x) then { $y } else { $z } to me is in the same spirit.

The cryptic aspect is ameliorated by ? being reminiscent of a question and therefore suggesting a conditional (albeit one preceding the ? in this case), and - more importantly - being a potentially familiar construct from several other languages, with the same fundamental semantics.

You don't get the same benefit with $x -then $y -else $z: it is not familiar, and that a conditional should go before -then is not obvious.

Also, while many PS operators do have symbolic names, many do not: * / + % ...
These are inherently cryptic too, we just don't perceive them that way anymore, because they are so familiar and ubiquitous.

My sense is that $x ? $y : $z too is already familiar to many, and will become even more so once introduced into the language, given the frequent need for concise conditionals.

@BrucePay

This comment has been minimized.

Show comment
Hide comment
@BrucePay

BrucePay Apr 24, 2018

Member

Note that you'll be pretty limited in what you can specify with this operator. In particular you won't be able to use commands unless you wrap them in parenthesis:

(test-path foo.txt) ? (get-content foo.txt) : (get-content bar.txt)

compared with

if (test-path foo.txt) {get-content foo.txt} else {get-content bar.txt}

or

get-content ((test-path foo.txt) ? "foo.txt" : "bar.txt")

vs

get-content $(if (test-path foo.txt) {"foo.txt"} else {"bar.txt"})

To my mind, there is little advantage in terms of brevity and a distinct disadvantage in readability. The ternary operator is far less interesting when you have an expression oriented language. When I started on the language 16 years ago, adding the ternary operator seemed obvious coming from a C background. Now I'm glad we never added it. It just feels like clutter.

Member

BrucePay commented Apr 24, 2018

Note that you'll be pretty limited in what you can specify with this operator. In particular you won't be able to use commands unless you wrap them in parenthesis:

(test-path foo.txt) ? (get-content foo.txt) : (get-content bar.txt)

compared with

if (test-path foo.txt) {get-content foo.txt} else {get-content bar.txt}

or

get-content ((test-path foo.txt) ? "foo.txt" : "bar.txt")

vs

get-content $(if (test-path foo.txt) {"foo.txt"} else {"bar.txt"})

To my mind, there is little advantage in terms of brevity and a distinct disadvantage in readability. The ternary operator is far less interesting when you have an expression oriented language. When I started on the language 16 years ago, adding the ternary operator seemed obvious coming from a C background. Now I'm glad we never added it. It just feels like clutter.

@mklement0

This comment has been minimized.

Show comment
Hide comment
@mklement0

mklement0 Apr 24, 2018

Contributor

In particular you won't be able to use commands unless you wrap them in parentheses:

That applies to any PowerShell operator.

Not all PowerShell statements involve commands, and when they do, users already know that (...) is the price of admission for commands (in most cases).

In your own example, to me ((test-path foo.txt) ? "foo.txt" : "bar.txt") beats $(if (test-path foo.txt) {"foo.txt"} else {"bar.txt"}), both for the obscurity of needing $(...) and the noise introduced by the curly braces.

Contributor

mklement0 commented Apr 24, 2018

In particular you won't be able to use commands unless you wrap them in parentheses:

That applies to any PowerShell operator.

Not all PowerShell statements involve commands, and when they do, users already know that (...) is the price of admission for commands (in most cases).

In your own example, to me ((test-path foo.txt) ? "foo.txt" : "bar.txt") beats $(if (test-path foo.txt) {"foo.txt"} else {"bar.txt"}), both for the obscurity of needing $(...) and the noise introduced by the curly braces.

@rkeithhill

This comment has been minimized.

Show comment
Hide comment
@rkeithhill

rkeithhill Apr 24, 2018

Contributor

Maybe it's just me but I find this:

get-content ((test-path foo.txt) ? "foo.txt" : "bar.txt")

way easier to visually scan/parse than this:

get-content $(if (test-path foo.txt) {"foo.txt"} else {"bar.txt"})

My only complaint about this issue is that if you're going to do ternary ?: then you should also do null-coalescing ??:

$logDir = $env:LogDir ?? "$PSScriptRoot\Log"

I've seen a number of Invoke-Ternary and Invoke-NullCoalescing implementations in the wild (e.g. https://github.com/dahlbyk/posh-git/blob/master/src/Utils.ps1#L12). That kind of indicates there is a general desire for such a feature baked into the language.

Contributor

rkeithhill commented Apr 24, 2018

Maybe it's just me but I find this:

get-content ((test-path foo.txt) ? "foo.txt" : "bar.txt")

way easier to visually scan/parse than this:

get-content $(if (test-path foo.txt) {"foo.txt"} else {"bar.txt"})

My only complaint about this issue is that if you're going to do ternary ?: then you should also do null-coalescing ??:

$logDir = $env:LogDir ?? "$PSScriptRoot\Log"

I've seen a number of Invoke-Ternary and Invoke-NullCoalescing implementations in the wild (e.g. https://github.com/dahlbyk/posh-git/blob/master/src/Utils.ps1#L12). That kind of indicates there is a general desire for such a feature baked into the language.

@mklement0

This comment has been minimized.

Show comment
Hide comment
@mklement0

mklement0 Apr 24, 2018

Contributor

@rkeithhill: I agree; re null-coalescing: there is an issue already, which also covers null-soaking: #3240

Contributor

mklement0 commented Apr 24, 2018

@rkeithhill: I agree; re null-coalescing: there is an issue already, which also covers null-soaking: #3240

@tibmeister

This comment has been minimized.

Show comment
Hide comment
@tibmeister

tibmeister Jul 14, 2018

So the argument of less experienced coders wouldn't be able to easily use a null-coalescing or ternary code statement I think is a little short-sighted. There's no requirement to use these "advanced" features and if a programmer can't read the code because they lack the experience, then personally that's my cue that I need to learn something new really quick.
When I first saw a ternary in C# I was like WTF is this fresh hell MS has introduced, until I took a few minutes to read up on this, which then I was hooked.

Practical example in POSH, I have this line:
$mc_object = ($Message.fields | where Name -Match appname).Content
which is fine and dandy except when the $Message object does not have a filed property that has a name of "app name", and since I don't control the JSON formatted data this comes from, I have to determine what to do.

So with a null-coalesce operations all I have to do is add ?? "" at the end to make sure that the $mc_object always was a valid string, even if the original setter was null. Otherwise, I would have to do some different techniques to accomplish what 4 keystrokes can do, such as
$mc_object = if(-not ($Message.fields | where Name -Match appname).Content)){""}else{($Message.fields | where Name -Match appname).Content}
That to me is very unreadable, but then I can clean this up a little bit by doing something like this, which is what I am doing.
$mc_object = ($Message.fields | where Name -Match appname).Content if(-not $mc_object){$mc_object = ""}

While not terrible, it's a heck of a lot more than 4 keys being pressed to null-coelasce.

Anyhow, I definitely vote for ternary and null-coelasce in Powershell, it adds some very advanced features and just makes Powershell more compelling to use in the "core" form on any system.

tibmeister commented Jul 14, 2018

So the argument of less experienced coders wouldn't be able to easily use a null-coalescing or ternary code statement I think is a little short-sighted. There's no requirement to use these "advanced" features and if a programmer can't read the code because they lack the experience, then personally that's my cue that I need to learn something new really quick.
When I first saw a ternary in C# I was like WTF is this fresh hell MS has introduced, until I took a few minutes to read up on this, which then I was hooked.

Practical example in POSH, I have this line:
$mc_object = ($Message.fields | where Name -Match appname).Content
which is fine and dandy except when the $Message object does not have a filed property that has a name of "app name", and since I don't control the JSON formatted data this comes from, I have to determine what to do.

So with a null-coalesce operations all I have to do is add ?? "" at the end to make sure that the $mc_object always was a valid string, even if the original setter was null. Otherwise, I would have to do some different techniques to accomplish what 4 keystrokes can do, such as
$mc_object = if(-not ($Message.fields | where Name -Match appname).Content)){""}else{($Message.fields | where Name -Match appname).Content}
That to me is very unreadable, but then I can clean this up a little bit by doing something like this, which is what I am doing.
$mc_object = ($Message.fields | where Name -Match appname).Content if(-not $mc_object){$mc_object = ""}

While not terrible, it's a heck of a lot more than 4 keys being pressed to null-coelasce.

Anyhow, I definitely vote for ternary and null-coelasce in Powershell, it adds some very advanced features and just makes Powershell more compelling to use in the "core" form on any system.

@TheIncorrigible1

This comment has been minimized.

Show comment
Hide comment
@TheIncorrigible1

TheIncorrigible1 Jul 24, 2018

@lzybkr I have yet to see anyone actually adopt $PSItem. I work on a decently large team of people who write scripts and they still all use $_, even the less-experienced PowerShell users.

@tibmeister or....

if (-not ($mc_object = $Message.fields.where{$_.Name -match 'appname'}.Content)) {
    $mc_object = ''
}

Assignments as expressions will passthru, but are incredibly unreadable.

TheIncorrigible1 commented Jul 24, 2018

@lzybkr I have yet to see anyone actually adopt $PSItem. I work on a decently large team of people who write scripts and they still all use $_, even the less-experienced PowerShell users.

@tibmeister or....

if (-not ($mc_object = $Message.fields.where{$_.Name -match 'appname'}.Content)) {
    $mc_object = ''
}

Assignments as expressions will passthru, but are incredibly unreadable.

@RichardSiddaway

This comment has been minimized.

Show comment
Hide comment
@RichardSiddaway

RichardSiddaway Jul 25, 2018

I regularly use $psitem

Over a number of years of judging Scripting Games $psitem was used a lot in the answers

RichardSiddaway commented Jul 25, 2018

I regularly use $psitem

Over a number of years of judging Scripting Games $psitem was used a lot in the answers

@mklement0

This comment has been minimized.

Show comment
Hide comment
@mklement0

mklement0 Jul 25, 2018

Contributor

@RichardSiddaway: If we look on Stack Overflow at PowerShell-tagged questions asked since 2014, we get (as of this writing):

$PSItem was introduced in v3 in September 2012, so to be safe I chose 2014 as the start date for the query.

While this is not an exact metric, - the site doesn't allow you to search for $_ or _, unfortunately, and I'm sure there are questions that contain neither $_ nor $PSItem, and pre-v3 questions are still being asked - I still think it's safe to conclude that $_ is used far more frequently than $PSItem.

However, the larger point is that there's no need to choose:

Just as $PSItem and $_ happily coexist, so do ForEach-Object and %, Where-Object and ?, ...:

$a ? $b : $c can coexist with if ($a) { $b } else { $c } / $(if ($a) { $b } else { $c })

As for recapping the positive reasons to introduce $a ? $b : $c

  • more concise, less visually cluttered, using syntax familiar to most programmers.

  • more efficient, because there is no need for $(...), which if statements need in order to allow their use as part of a larger expression (which is noisier, less efficient, and can have unintended side effects)

As for the obscurity concern:

Someone writing expressions (as opposed to merely invoking commands with (simple) arguments) can be assumed to have some developer experience, and my (anecdotal) sense is that most developers at least recognize $a ? $b : $c as a condensed if statement, whether or not they actively use it.

Even if they don't, however, it's easily explained, and aside from the use of non-obvious symbols, the conceptual complexity is the same as that of an if ($a) { $b } else { $c }, and even less than
$(if ($a) { $b } else { $c })

Assuming that it catches on once introduced - certainly, this discussion shows that there's demand for it - encountering it frequently will make it a familiar and easily recognizable idiom (that is more concise, easier to type and, at least to me and a few others here, more readable) - just like most people seem to prefer $_ over $PSItem.

Contributor

mklement0 commented Jul 25, 2018

@RichardSiddaway: If we look on Stack Overflow at PowerShell-tagged questions asked since 2014, we get (as of this writing):

$PSItem was introduced in v3 in September 2012, so to be safe I chose 2014 as the start date for the query.

While this is not an exact metric, - the site doesn't allow you to search for $_ or _, unfortunately, and I'm sure there are questions that contain neither $_ nor $PSItem, and pre-v3 questions are still being asked - I still think it's safe to conclude that $_ is used far more frequently than $PSItem.

However, the larger point is that there's no need to choose:

Just as $PSItem and $_ happily coexist, so do ForEach-Object and %, Where-Object and ?, ...:

$a ? $b : $c can coexist with if ($a) { $b } else { $c } / $(if ($a) { $b } else { $c })

As for recapping the positive reasons to introduce $a ? $b : $c

  • more concise, less visually cluttered, using syntax familiar to most programmers.

  • more efficient, because there is no need for $(...), which if statements need in order to allow their use as part of a larger expression (which is noisier, less efficient, and can have unintended side effects)

As for the obscurity concern:

Someone writing expressions (as opposed to merely invoking commands with (simple) arguments) can be assumed to have some developer experience, and my (anecdotal) sense is that most developers at least recognize $a ? $b : $c as a condensed if statement, whether or not they actively use it.

Even if they don't, however, it's easily explained, and aside from the use of non-obvious symbols, the conceptual complexity is the same as that of an if ($a) { $b } else { $c }, and even less than
$(if ($a) { $b } else { $c })

Assuming that it catches on once introduced - certainly, this discussion shows that there's demand for it - encountering it frequently will make it a familiar and easily recognizable idiom (that is more concise, easier to type and, at least to me and a few others here, more readable) - just like most people seem to prefer $_ over $PSItem.

@tibmeister

This comment has been minimized.

Show comment
Hide comment
@tibmeister

tibmeister Jul 26, 2018

mklement0, you have a very awesome example and I think demonstrates the point clearly, thank you.

tibmeister commented Jul 26, 2018

mklement0, you have a very awesome example and I think demonstrates the point clearly, thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment