Closed daxian-dbw closed 4 years ago
Implicit line continuance for
?
is supported
Hooray for implicit line continuance! π
A new bool field
ForceEndNumbeOnTernaryOpChars
is added toTokenizer
and used when scanning number tokens. When the tokenizer is current inExpression
mode,ForceEndNumbeOnTernaryOpChars
istrue
, and the current char is?
or:
, we force ending the number token.
The description of this PR includes a mispelling. The bool field should be ForceEndNumberOnTernaryOpChars
.
Also that flag sounds a good candidate for an Optional Feature. Even with that proposal just in RFC, it would be useful/helpful to consider how it could be used for this optional way to parse ternary operators, so that we work out any kinks in the proposed optional feature design.
The description of this PR includes a mispelling. The bool field should be
ForceEndNumberOnTernaryOpChars
.
@KirkMunro Thanks for pointing out this. @kvprasoon also caught it. It has been fixed.
@daxian-dbw Does the fact that Where-Object
has an alias of ?
get in the way of implicit continuance using ?
at the start of a line? I don't think it's likely, because you would have to be using -InputObject
in that scenario, which means working with a collection in Where-Object
which is unlikely; however, something obscure like this is possible:
$c = @(gps -name pwsh)
$b = $StopProcess -eq $true
? -InputObject $c Count -gt 0
| % {if ($b) {spps $c -whatif}}
That's messy, and hopefully nothing like that exists in scripts, but I wanted to call out the potential conflict with the ?
alias so that the potential issue is known/documented.
@KirkMunro Good catch. The continuation of ?
will break ? -InputObject $c Count -gt 0
in that case, so it will be a potential breaking change.
Personally, I'm not too worried about it. That's a very obscure use of Where-Object
and I presume the possibility of actually breaking any scripts is very low.
But we do need the committee review to approve this.
@daxian-dbw That's my take as well. I at least wanted to capture it so that if this moves forward with implicit continuance (I hope it does), the obscure break would be known/documented.
With ?
already existing as a widely used alias, and Powershell often remaining distinct to common operator designs like ==, >, ||, &&
, would it make sense to consider using something more verbose? So following the design of the rest of the powershell in going for verbosity and readability over shortness?
For example, instead of:
$a -eq $b ? $a : $c
something like -then
/ -else
?
$a -eq $b -then $a -else $c
Or to save on included -then, but forgot -else errors caused by the above, treat it much like -replace 'a','b'
/.replace('a','b')
, and have an explicit -ternary a,b
/ .ternary(a,b)
?
($a -eq $b) -ternary $a, $c
($a -eq $b).ternary($a, $c)
Finally having ternaries would be great, and so would improving consistency with other languages, but I would be surprised if two uses for ?
didn't cause confusion and readability issues compared to a more verbose, but still short, operator/method.
I proposed -then
/-else
here so you can see the subsequent discussion.
Having spent the past 2 years away from C# and using languages without the cond ? then : else
syntax (mostly F# and Rust), I think -then
/-else
is highly suitable syntax for PowerShell.
I find the arguments for familiarity weak at best - those used to the familiar syntax are likely to get used to the PowerShell syntax quickly, and for those not familiar, I think the readability of a slightly more verbose syntax is really important.
Maybe this PR could enable both forms and keep the feature experimental until folks have some experience with it.
One downside that is significant (to me at least) is that using -then
/-else
syntax would mean implicit line continuation would be a more risky breaking change, since users could have a command called -then
or -else
. That may not seem like a big deal, but it will likely be common for users of this feature to want it to span multiple lines (based on this being common in other programming languages), and PowerShell could only support that natively with -then
or -else
at the end of the line, which is not as readable as it would be with them at the start of a line.
It's curious that a hypothetical command -then
or -else
is more worrisome than an actual command ?
.
Well, the actual ?
command is an exception in that regard, because:
-InputObject
, which will be an array unless you're using Where-Object
to filter a single objectWhere-Object
to start a pipeline are obscure at best, and I've never seen it in practice in 12+ years of working with PowerShell.It's such an obscure scenario, I'm highly skeptical that anyone will be affected by it.
On the other hand, with respect to the potential for -then
or -else
commands, I have literally no data to work with, so I don't have as high of a confidence level that there wouldn't be a conflict/break somewhere.
Just a question: Why?
why would this ever be a good idea for Powershell? what would it add? I mean, I get it. Its somewhat easier to write than a simple "if" and with this you would also get Elvis notation, that could possibly be helpful, but both the language overhead (if you come from a non-programmer background, this makes no sense) and the possibility that this could break the tokenizer if not properly implemented (especially with ? being used for where-object as stated earlier in this thread), i cannot see any way that this would help the language progress in any way.
@WithHolm Because the ternary operator is one of the most concise and neat abstractions available in many languages. While it could break the tokenizer if not properly implemented, I'd argue that it should be properly implemented.
I'm probably missing something obvious, but are you sure there would be a line continuation issue for the -then -else suggestion? Currently you can do the following with multiple operators over multiple lines:
'string' -replace
'ing',
'new_string_ending' -split
'_'
so presumably a consistent option would be the same format for -then -else?
$a -eq $b -then
$a -else
$c
Assuming that this isn't viable, why not -ternary / .ternary()
?
Much like with the current behaviour of -replace / .replace()
, which can be spread across lines, using this existing operator format would be consistent with existing behaviours, the below works with replace
('test').Replace(
'te',
'be'
)
# output: best
'test' -Replace(
'te',
'be'
)
# output: best
'test' -Replace
'te',
'be'
# output: best
$a = 'one'
$b = 'two'
$a + $b -replace
'et',
''
# output: onwo
So why not follow the same pattern for ternary?
# Same line
$a -eq $b -ternary $a,$c
($a -eq $b).ternary($a,$c)
# multi-line
$a -eq $b -ternary
$a,
$c
($a -eq $b).ternary(
$a,
$c
)
@WithHolm:
if you come from a non-programmer background, this makes no sense
If you come from a non-programmer background, a lot of PowerShell will initially make no sense. Then you learn how it works, and use what you have learned. This is no different.
the possibility that this could break the tokenizer if not properly implemented
Literally any feature can break something if it is not properly implemented. If our condition for adding something to the language was whether or not it could break something if it was not properly implemented, the language would never change.
(especially with ? being used for where-object as stated earlier in this thread)
As also stated earlier in the thread, there is a very, very low probability that the potential collision with Where-Object
would occur because of how that cmdlet is used. It can only collide if the ?
alias happens to be used to start a pipeline, which while possible, literally nobody ever does, and that line would also have to be on a line immediately following a conditional expression. The chance that would break something is almost non-existent (and may actually be non-existent).
why would this ever be a good idea for PowerShell? what would it add? i cannot see any way that this would help the language progress in any way.
Ternary operator support has been requested by many PowerShell users, so there seem to be plenty of folks who would use this. I've wished I had ternary operator support many times when it would make my code more elegant and easier to read. It doesn't absolutely have to come in the form of ?:
for me, but as someone who works with C#, I do like that syntax a lot.
@Taoquitok:
I'm probably missing something obvious, but are you sure there would be a line continuation issue for the -then -else suggestion?
The line continuation issue potentially comes up when you're using implicit continuance, with the operators at the start of a line.
Since @SteveL-MSFT invited commentary on twitter (I thought this was a "done deal")β¦ overall, I don't see the point. Yes, a ternary operator is more "terse" than the "if(){}else{}" but not much. (And that terseness makes it slightly less readable to a new PS user or any user who is from a non-C language.) And the 'if' statement has been around forever. And there are no concerns about multi-line parsing or operator precedence or operator confusion.
If there is going to be a ternary operator, my personal opinion is that using -then/-else is more PowerShell-y, less confusing to novice users, and eliminates any operator confusion. Recognizing, of course, at that point you've lost most terseness (perhaps all, depending on your style):
Existing:
if($b -eq 1) { 1 } else { 0 }
Two -then/-else styles
$b -eq 1 -then { 1 } -else { 0 }
$b -eq 1 -then 1 -else 0
Two ?: styles
$b -eq 1 ? 1 : 0
$b -eq 1 ? { 1 } : { 0 }
Although based on @BrucePay comments in https://github.com/PowerShell/PowerShell/issues/3239#issuecomment-383776193, it seems that some of those '{}' blocks will actually have to be '()' expressions for ternary statements - which I would find extremely confusing.
One more comment, copied from https://github.com/rust-lang/rust/issues/1698#issuecomment-3706301: "I use the ternary operator a lot in C++, but I think the syntax is poor. You have to spot the ? and : in the middle of a sea of other tokens to see that it's even a conditional." Using -then/-else addresses that visibility issues.
If the decision is to go with $b -eq 1 -then 1 -else 0
then it is not worth making the change IMO. The whole point of the ternary operator is to cut the ceremony for a simple concept: $foo = $var ? $var : "default"
. And FWIW, I'd like to see that ceremony cut further with a null-coalescing
operator e.g. $foo = $var ?? "default"
.
Would folks be happier with Posix param substitution for this scenario e.g.: $foo = ${var:-'default'}
(or however that concept would map into PowerShell). I figured $foo = $var ? $var : "default"
would be easier to understand and cover one of the more common cases for assignment.
Yeah, the point here is the brevity, really. I think the proposed $condition ? $ifTrue : $ifFalse
is the best way to go that we currently have on the table. -then
and -else
do fit in a little better with PowerShell operators, but in terms of terseness they're essentially equivalent to just doing if ($condition) { $ifTrue } else { $ifFalse }
.
Having them as distinct, brief symbols, which are very visually distinct (not word-operators, etc) is the value here; they're small, easily distinguished markers in most cases.
I think it would be easy for scripters to grasp the meaning of $condition ? <true-expr> : <false-expr>
.
If we go with -then -else
for ternary operator, then for the null-coalescing operator, maybe something like -ifnull
, but for null-conditional operators, we just have to use the cryptic symbols ?.
and ?[]
, which has the ?
in them. Considering that, it's better to be consistent.
I think @Jaykul (Joel Bennet, don't know his github name) had a great idea with -then/-else on twitter (https://twitter.com/Jaykul/status/1151913597930496000):
Would the -then -else still have to be used together?
Can -else also be a null-coalescing operator?
$ComputerName = $ComputerName -else "localhost"
Can -then be a conditional set operator?
$ComputerName = $ComputerName -eq "." -then "localhost"
These are very consistent - and very "PowerShell-y" and, in my opinion, are immediately understandable. And eliminates the cryptic characters.
I guess at some point it's about philosophy. PS isn't C or C#. You can move it toward them, which ?: does, or continue with its scripting roots, which tends to be a bit more verbose and more accessible to a non-programmer.
While I think such a suggestion is worth considering, I don't think it bears relation to this PR. -then
and/or -else
could be implemented as binary operators for those cases. In fact, I'd love those ones to be available. But I don't think they should be the same / overlapping with a ternary syntax; would love to talk over some possibilities of those in a separate issue, as I don't think their possible implementation is particularly entwined to this PR itself.
This PR introduces a ternary operator syntax. This structure has never been used in PS before, and it will be (as of this PR, anyway) the only instance of a ternary syntax in PS. As such, I think it makes more sense to break away from (somewhat) traditional -operator
syntax; retaining that syntax will most likely lead to more confusion about the ways you can use the operators or not.
I think that while, yes, it's important to have features that are verbose for new scripters, we already have that — if ($condition) { $val1 } else { $val2 }
conveys that perfectly well. This would simply be a more brief version of that, just like how we have both Get-ChildItem
and gci
. Making it more verbose defeats the purpose of the PR, I think. We already have a "more verbose" version available. π
@daxian-dbw has already entwined ternary with null-coalescing and null-conditional, in the comment immediately preceding mine. Which is why I mentioned it. I wasn't being hypothetical.
You wrote "retaining that syntax will most likely lead to more confusion about the ways you can use the operators". What's the justification for that statement? The operators do not currently exist, so I don't see how introducing them leads to confusion. Their meaning is far more clear, in my opinion at least, than ?:.
I... am not sure what you're referring to, I'm afraid. Both null-coalescing and null-conditional syntaxes have similar but distinct syntaxes in the forms he proposed. With @Jaykul's proposal that you're talking about, the exact same operators would be used in both cases, creating an avenue for confusion.
All -eq
style operators currently are binary operators -- they take a left hand and right hand side operand, and they don't take anything else into account.
The proposal here is to implement a syntax that depends on three arguments instead of two, with a short-circuiting behaviour such that the branch that doesn't get followed never gets evaluated, so you don't have to worry about side effects. There are currently zero operators that work this way in PowerShell, so I think that grouping it in with a -then
/ -else
syntax is likely to lead to confusion in usage, with the behaviour unclear when one or both is used, depending how you approach it.
I simply thing that you're trying to cram a bit much in the one box here. Some PS operators have variant behaviours, but that's always due to a difference in their arguments. None of them currently change based on whether you supply another operator. The -eq
operator doesn't alter its behaviour if you later use -ne
-- this would be a very unusual thing.
I think it makes the most sense to have -then
and -else
be separate operators, not rolled into the ternary syntax here.
I understand what you are saying. The only avenue for confusion I see is specifying -else/-then as opposed to -then/-else. Which would be a syntax error, just as it is if you specified a standalone else today. All of these are interpreted based on where they are used in the language grammar. That doesn't change. ? is location dependent, : is location dependent, etc.
I would point out that operator precedence does directly affect how an expression evaluates, both mathematically and logically. So -eq can have an unexpected result, as I'm sure we've all been surprised the first time we write "-not $a -eq $b".
Regardless, I am actually not particularly invested in this. I wrote my first ternary in 1981. The syntax doesn't confuse me and I don't think it's particularly difficult to understand; but it is terse and can be difficult to read.
I just think PS should stay PS and not try to be a C-family lookalike. I don't think ?: is PowerShell-y at all.
Peace.
We've definitely had the debate over whether or not to include this already -- but when examples get non-trivial and start wrapping lines, I'm not sure how a person whose first and only language is PowerShell is supposed to understand what's going on.
They may know ?
is Where-Object
and they might have learned that :label
is used for breaking out of looping constructs, but how will they read this?
$Script:Config.RemoteServer -eq $ParameterValue
# Describe what this does
? (Invoke-Command -Cn $Script:Config.RemoteServer {
<# do things #>
})
# The parenthesis here are required if it's a command, right?
: (Do-SomethingElse)
After all, although it doesn't appear to do anything, ? (...)
is perfectly valid syntax that actually does roughly the same thing as (...)|out-null
right now:
? (iwr google.com -OutFile g.html -PassThru)
I'm not concerned you'll break that -- I'm asking how a user is supposed to know that's not what you're doing.
If you put the ":" on the following line, then suddenly the space or parenthesis after it is mandatory, right? Since otherwise :Whatever
is a valid command name? I don't know. It's giving me a headache.
Imagine how much worse it gets if you're mixing in some null-coalescing and null-conditional calls.
I think the -then
and -else
options are more in line with PowerShell syntax, especially if we overloaded -else
for null-coalescing: $x = 2 * ($Size -else $DefaultSize)
I've been thinking the main gain of a conditional operator is the fact that it more explicitly outputs a value, and doesn't require wrapping in $( )
in order to feed that value to a pipeline and doesn't confuse people when you assign it's output to a variable.
I'm not sure how often I'll use it, because it's readability is painful for PowerShell. For anything non-trivial, you're going to loose most of the benefit if you have to wrap each clause in a parenthesis...
I just think PS should stay PS and not try to be a C-family lookalike.
But if {} else {}
, do/while/for/foreach() {}
, switch () {}
and try/catch/finally
all came from C-family languages. I mean PS could have used if fi
and case ... esac
(ugh, just threw up in my mouth a little on that one). They specifically chose in the early days to follow C#'s lead WRT syntax.
The whole -<operator>
approach was hotly debated in the early Monad beta days but folks (rightly) couldn't get past using >
for greater than when in every other shell in the world, that's a redirection operator.
Given this conversation, I'm shocked we ever got try/catch/finally
into the language. We had trap {}
, why add this new-fangly developer thing to catch exceptions?
@rkeithhill if there had been an RFC process, you probably wouldn't have gotten try/catch unless someone implemented it first and demonstrated the performance impact π
As an "inclusionist" I want both styles of syntax.
The "C" like one, and the verbose PowerShell one.
The terse one I'd use at the command prompt. The verbose one I'd use in a script.
I think the pith motivation for a ternary is indicative; it's like an alias form of $(if / else)
. In which case we should favour brevity, so ?
and :
. That also means we can provide ?.
and ?[]
with null-soaking semantics (although the ternary condition should have the usual boolean-coercion falsey semantics) with some syntactical consistency.
Holistically, PowerShell has a lot of differences from C# and I don't think it should just copy along on that front, but it's clearly in the C-like syntax family I think, so we can justify the ?
and :
there as well.
That wouldn't stop us from adding something like -then
and -else
later to behave like &&
and ||
do in JavaScript/TypeScript (which also have ternary operators).
On the newline complexity thing @Jaykul mentions, I'm again motivated by the brevity/pith thing. In Python lambda
s can only be on a single line (because if it's longer than that you should write a function). Perhaps in PowerShell, if your ternary needs clever newline mangling, it's time to write an if { } else { }
.
I think it's also confusing with $? usage.
$? ? $? : '?'
Why not use unused operators && and || or something else?
I understand while reading all of this that the tenerary, is not really a thing that is going away. people coming from a developer background wishes to have this, while powershell needs to be verbose in order to be "newbie" friendly. A "get used to this" approach is a bad idea and will just create a bigger hurdle for new users.
However.. ive been thinking: How about using it as a cmdlet, that can be pielined and hopefully with a alias (like Foreach or Where)? This makes the property easy to use, follows already existing powershell framework and does not require new defintions for the core. Most importantly it can be used in the command prompt!
$var = @{
prop = "Yes!"
}
$var|Ternary-object{$_.prop, "no"}
#small t, as a |T looks ugly, but it could also be some symbol
$var|t prop, "no"
Not sure if this in the realm of ternary, but is there a semantic for $foo=$bar if ($foo). Ruby has nice syntactical sugar along those lines.
Not sure if this in the realm of ternary, but is there a semantic for $foo=$bar if ($foo). Ruby has nice syntactical sugar along those lines.
@rismoney That is way outside the scope of the original ternary discussion. As a ruby user myself, that requires a whole other discussion.
@WithHolm
How about using it as a cmdlet, that can be pielined and hopefully with a alias (like Foreach or Where)? This makes the property easy to use, follows already existing powershell framework and does not require new defintions for the core. Most importantly it can be used in the command prompt!
Pipelines are not really compatible with expression syntax; an expression is only usable at the start of a pipeline. As this is intended to be used in expression syntax, that doesn't jive particularly well.
However, you could indeed make such a function relatively easily for the purposes you describe. There's nothing stopping you from doing so. Also, even with the expression syntax... you can still use it at the prompt, not sure what you mean there. All PowerShell is valid from the PowerShell prompt.
@rismoney
Why not use unused operators && and || or something else?
Because those are used for other things in Bash by convention, and intended more for use with commands rather than expressions. That is also being implemented in a currently-being-worked-on PR from @rjmholt.
@Jaykul
I'm not concerned you'll break that -- I'm asking how a user is supposed to know that's not what you're doing.
If you put the ":" on the following line, then suddenly the space or parenthesis after it is mandatory, right? Since otherwise :Whatever is a valid command name? I don't know. It's giving me a headache.
Imagine how much worse it gets if you're mixing in some null-coalescing and null-conditional calls.
I think the -then and -else options are more in line with PowerShell syntax, especially if we overloaded -else for null-coalescing: $x = 2 * ($Size -else $DefaultSize)
I don't disagree with your stance here, but I do think making the operators look too much like existing operators is asking for a difficult time for people just learning it. Ternary is not a currently available syntax in PS, and I think we'd be lax to make it look too similar to currently available syntax. It is a different syntax, and it's going to behave differently to existing binary operators; thus, it should look different to set the correct expectation.
And, as with anything new we introduce -- there's always a bit of a barrier for new folks whenever something new is added. That's why we document features; I don't think "people aren't immediately going to be familiar with it" is a great place to start at -- PowerShell as a very concept was initially so alien that it required a whole manifesto before there was sufficient buy-in for it to even get started.
PowerShell strives to be as accessible as possible, but also as flexible as possible, given that it's a shell. Elastic syntax is great for this; I'd generally consider this more as a more brief way to write if ($a) { $then } else { $otherwise }
that we can already work with. Yes, it's not perfectly equivalent as we want it to live in the realm of expressions rather than keywords or commands, but it is effectively the expression equivalent -- and the if/else syntax remains available.
On the newline complexity thing @Jaykul mentions, I'm again motivated by the brevity/pith thing. In Python
lambda
s can only be on a single line (because if it's longer than that you should write a function). Perhaps in PowerShell, if your ternary needs clever newline mangling, it's time to write anif { } else { }
.
PowerShell is very verbose, so you're really reducing the usefulness of a ternary operator if you're forcing it to be used only on a single line. Even in C#, which is much more pithy than PowerShell, I span ternary operators across multiple lines all the time because (a) it reads very well, and (b) it is much easier to see what will happen when the code runs. e.g.
Runspace = _streamingHost != null
? RunspaceFactory.CreateRunspace(_streamingHost, iss)
: RunspaceFactory.CreateRunspace(iss);
Carrying that same example into PowerShell as a single line would give us this:
$Runspace = $streamingHost -ne $null ? [RunspaceFactory]::CreateRunspace($streamingHost, $iss) : [RunspaceFactory]::CreateRunspace($iss)
And that's with an example that's not even indented.
Compare that with:
$Runspace = $streamingHost -ne $null
? [RunspaceFactory]::CreateRunspace($streamingHost, $iss)
: [RunspaceFactory]::CreateRunspace($iss)
If implicit continuance is not supported, that would look like this:
$Runspace = $streamingHost -ne $null ?
[RunspaceFactory]::CreateRunspace($streamingHost, $iss) :
[RunspaceFactory]::CreateRunspace($iss)
That's a little awkward. Plus with these examples, they aren't even using actual PowerShell commands, which could result in the ternary operator symbols being far off to the right where it becomes quite difficult to see what is going on at a glance.
Going back to the -then|-else
proposal, I like that syntax as an option, and could see myself using:
$Runspace = $streamingHost -ne $null `
-then [RunspaceFactory]::CreateRunspace($streamingHost, $iss) `
-else [RunspaceFactory]::CreateRunspace($iss)
But to get that syntax I'd need the backticks I used here or some sigil to indicate I want multi-line continuation for that command since -then
and -else
could be command names already in place in people's scripts.
If that was the only option for ternary, I'd just do this instead:
$Runspace = if ($streamingHost -ne $null) {
[RunspaceFactory]::CreateRunspace($streamingHost, $iss)
} else {
[RunspaceFactory]::CreateRunspace($iss)
}
@jaykul For your earlier example of ternary use with comments and Invoke-Command
, I think that highlights a few best practices that should come out with ternary support:
We could consider not allowing comments inside of a ternary (that's just a boolean flag to switch in the code) if we feel they'll cause more confusion than they're worth.
I think the right approach is to be very conservative in extending the language. So what is the main question that we cannot do without this operator by existing means?
Today I donβt see why itβs definitely better than $var = if ( ... ) { ... } else { β¦ }
I think the right approach is to be very conservative in extending the language
@iSazonov personally I'm very much with your there. I think here basically there's been an ask on this one for a while and in a restricted sense it's not a huge feature. Under ordinary circumstances I think it's worth opting on the conservative side, but in this case I think it's a nice inclusion and unusually for PowerShell, it can be implemented in a way that doesn't really step on any toes.
I span ternary operators across multiple lines all the time because (a) it reads very well, and (b) it is much easier to see what will happen when the code runs
@KirkMunro I totally agree with you there — I do the same in C#. However, my feeling there is:
?
-operator line continuation, the pre-?
-operator continuation is both trickier to implement and collides with ?
as a command name (which I know is unusual, but still non-zero)?
/:
with no pre-continuation, we have a ternary with a philosophy to encourage complex expressions to be more verbose and self-documenting, and don't have to deal with the command/operator conflict problemI personally imagine ?
/:
to be an ergonomic improvement for interactive and on-the-fly PowerShell usage, and something that PSScriptAnalyzer will likely encourage you to replace with if
/else
in scripts deployed for long-term or shared usage. I think if you want a conditional expression and you're going to spend more characters on if
/else
than the body then that's what the ternary serves.
I'll admit that I don't have a strong or dogmatic feeling on this particular feature (I also like -then
and -else
but I think they could be added separately as non-coercing truthy/falsey expression operators). But that leads me to think it's best to keep this feature smaller.
... and something that PSScriptAnalyzer will likely encourage you to replace with if/else in scripts deployed for long-term or shared usage.
@rjmholt Personally, I don't think PSScriptAnalyzer
should discourage the use of ternary operator in script. It's less nesting comparing to if {} else {}
, and could be cleaner in many cases.
So having ?/: with no pre-continuation...
It's pre-continuation or bust for this operator as far as I'm concerned, because without that it simply cannot add enough value to be useful, as I attempted to demonstrate in the examples above.
On the conflict/potential confusion with the ?
alias for Where-Object
, since aliases are not meant for use in scripts, I wonder if that helps reduce the potential for confusion with this operator, which would be for use in scripts. π€
Aliases, especially ? can be used in scripts and have been present in the language since inception. They are first class citizens.
@daxian-dbw
@KirkMunro Good catch. The continuation of
?
will break? -InputObject $c Count -gt 0
in that case, so it will be a potential breaking change. Personally, I'm not too worried about it. That's a very obscure use ofWhere-Object
and I presume the possibility of actually breaking any scripts is very low. But we do need the committee review to approve this.
Would it be feasible to fall back to command parsing if :
is not included?
e.g.
# Parse as ternary:
0 -eq 1
? "something"
: "nothing"
# Parse as binary expression; command; string constant:
0 -eq 1
? "something"
"nothing"
I haven't looked at what parts of resync are expensive, but if it's the actual act of falling back and not the saving of state, the impact should be minimal yeah?
@SeeminglyScience that example in particular is worrying to me; it's a naturally LR grammar construct that we must parse with an LL parser. We have to do arbitrary lookahead to cross the tokens of the first expression to know what kind of token and therefore what syntactic element we are looking at. That conflicts with the essential structure of the PowerShell parser.
Here is a fuller explanation of what I'm talking about.
I think that that PowerShell should use the same/similar syntax as Python.
$False if $x -eq 1 else $True
The more we debate this, the more the -then
and -else
options look better to me.
However, the more I think about that (and go and re-read @BrucePay's arguments for not adding this) the more I wonder why we are doing this at all. PowerShell doesn't really need to be terse.
But if people actually think the brackets make things unreadable, why not just ...
then
insteadOf course, we risk looking like VB instead C#, but if we can make the parser handle ?:
then certainly we can make it handle if then else
without braces?
It would basically be the same as adding the ternary operator with -then
and -else
except you'd have to write the if
keyword on the front. Putting that on the front should make most of the opposition about readability go away, and if we choose not to need the -
on the front of then
and else
then it works out to the same number of keystrokes π
$Runspace = if $streamingHost -ne $null
then [RunspaceFactory]::CreateRunspace($streamingHost, $iss)
else [RunspaceFactory]::CreateRunspace($iss)
Is definitely somewhat more readable than:
$Runspace = $streamingHost -ne $null
-then [RunspaceFactory]::CreateRunspace($streamingHost, $iss)
-else [RunspaceFactory]::CreateRunspace($iss)
And much more readable than:
$Runspace = $streamingHost -ne $null
? [RunspaceFactory]::CreateRunspace($streamingHost, $iss)
: [RunspaceFactory]::CreateRunspace($iss)
Of course, this does not actually solve any problems, and any change like this opens a can of worms with regards to additional syntax changes. In fact, this change as proposed, is clearly more complicated (in terms with colliding with existing syntax) and less useful (in terms of new functionality) than most syntax changes that could be proposed.
While I could maybe see adding -then|-else
operators, please do not add then
as a new keyword. PowerShell is not an if/then
language ala Basic. And at some point, unless you can achieve something close to the pithiness of ?:
then don't bother. We already have an existing solution which I do use, I just really don't like its verbosity/ceremony for such a simple concept.
Exactly. If you want the verbosity, we have if statements already, and there's nothing wrong with them.
The ternary is just an expressive shorthand. π€·ββ
The more we debate this, the more the
-then
and-else
options look better to me.
Some thoughts from someone busy teaching beginners and advanced users alike how to use PS to its fullest (and I may be terribly wrong with some observations yet am thrilled to see this lively public discussion prior to making changes to the language):
On the potential confusion because of the ?
alias for Where-Object
, I was thinking this weekend that we just need to change the way we think about ?
. Instead of just looking at ?
as an alias, we need to teach/look at it as a context-sensitive conditional check. What it checks and what happens as a result depends on the context in which it is used. This shouldn't be much more difficult to learn then an operator that can be used as either an unary operator or a binary operator.
I thought exactly the same this morning π ? complements the existing ?-alias very well.
Von meinem iPhone gesendet
Am 21.07.2019 um 14:19 schrieb Kirk Munro notifications@github.com:
On the potential confusion because of the ? alias for Where-Object, I was thinking this weekend that we just need to change the way we think about ?. Instead of just looking at ? as an alias, we need to teach/look at it as a context-sensitive conditional check. What it checks and what happens as a result depends on the context in which it is used. This shouldn't be much more difficult to learn then an operator that can be used as either an unary operator or a binary operator.
β You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.
PR Summary
Add support to ternary operator
<condition> ? <if-true> : <if-false>
. It results in aTernaryExpressionAst
.PR Context
Ternary operator has lower precedence than binary operator, so you can write
$a -eq $b ? "Hello World" : [int]::MaxValue
.Implicit line continuance for
?
is supported, so you can writeToday,
1?2:3
is parsed as a command name as1?2:3
is considered as a generic token by the tokenizer. We don't want to break that, but in certain situation, we know we are expecting an expression and a generic token like1?2:3
is not useful. In those cases, we want to make the ternary operator chars?
and:
to force ending a number token scan. A new bool fieldForceEndNumbeOnTernaryOpChars
is added toTokenizer
and used when scanning number tokens. When the tokenizer is current inExpression
mode,ForceEndNumbeOnTernaryOpChars
istrue
, and the current char is?
or:
, we force ending the number token. With this, we are able to write$a -gt 2?[int]::MaxValue:3
,${true}?3:1
, and-not 1?2:3
.Characters
?
and:
are valid chars for a variable name, so$varName?2:3
will be parsed as a variable. In order to write concise ternary expression with a variable condition, you can do this:${varName}?2:3
.This PR is a draft, not finished yet
Send out the draft PR to start collecting feedback. Also, exercise the CI builds to find regressions. Pending work:
VariableAnalysis
needs to be updated to account for the ternary operator.UpdatePosition
is correctly called at the right place.PR Checklist
.h
,.cpp
,.cs
,.ps1
and.psm1
files have the correct copyright headerWIP:
or[ WIP ]
to the beginning of the title (theWIP
bot will keep its status check atPending
while the prefix is present) and remove the prefix when the PR is ready.